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 rewindFormComponent(writer, cycle);
115
116 // This is for the benefit of the couple of components (LinkSubmit and RadioGroup) that allow a body.
117 else if (getAlwaysRenderBodyOnRewind())
118 renderBody(writer, cycle);
119 }
120 else if (!cycle.isRewinding())
121 {
122 renderFormComponent(writer, cycle);
123
124 if (getCanTakeFocus() && !isDisabled())
125 {
126 delegate.registerForFocus(
127 this,
128 delegate.isInError() ? ValidationConstants.ERROR_FIELD
129 : ValidationConstants.NORMAL_FIELD);
130 }
131
132 }
133 }
134
135 /**
136 * A small number of components should always render their body on rewind (even if the component
137 * is itself disabled) and should override this method to return true. Components that
138 * explicitly render their body inside
139 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} should leave this method returning
140 * false. Remember that if the component is {@link IFormComponent#isDisabled() disabled} then
141 * {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} won't be invoked.
142 *
143 * @return false; override this method to change.
144 */
145 protected boolean getAlwaysRenderBodyOnRewind()
146 {
147 return false;
148 }
149
150 protected void renderDelegatePrefix(IMarkupWriter writer, IRequestCycle cycle)
151 {
152 getForm().getDelegate().writePrefix(writer, cycle, this, null);
153 }
154
155 protected void renderDelegateAttributes(IMarkupWriter writer, IRequestCycle cycle)
156 {
157 getForm().getDelegate().writeAttributes(writer, cycle, this, null);
158 }
159
160 protected void renderDelegateSuffix(IMarkupWriter writer, IRequestCycle cycle)
161 {
162 getForm().getDelegate().writeSuffix(writer, cycle, this, null);
163 }
164
165 protected void setName(IForm form)
166 {
167 form.getElementId(this);
168 }
169
170 /**
171 * Returns false. Subclasses that might be required must override this method. Typically, this
172 * involves checking against the component's validators.
173 *
174 * @since 4.0
175 */
176 public boolean isRequired()
177 {
178 return false;
179 }
180
181 protected abstract void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle);
182
183 protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle);
184 }