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, but a few ({@link Hidden})
046         * override this method to return false.
047         */
048    
049        protected boolean getCanTakeFocus()
050        {
051            return true;
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            else if (!cycle.isRewinding())
119            {
120                renderFormComponent(writer, cycle);
121    
122                if (getCanTakeFocus() && !isDisabled())
123                {
124                    delegate.registerForFocus(
125                            this,
126                            delegate.isInError() ? ValidationConstants.ERROR_FIELD
127                                    : ValidationConstants.NORMAL_FIELD);
128                }
129    
130            }
131        }
132    
133        protected void renderDelegatePrefix(IMarkupWriter writer, IRequestCycle cycle)
134        {
135            getForm().getDelegate().writePrefix(writer, cycle, this, null);
136        }
137    
138        protected void renderDelegateAttributes(IMarkupWriter writer, IRequestCycle cycle)
139        {
140            getForm().getDelegate().writeAttributes(writer, cycle, this, null);
141        }
142    
143        protected void renderDelegateSuffix(IMarkupWriter writer, IRequestCycle cycle)
144        {
145            getForm().getDelegate().writeSuffix(writer, cycle, this, null);
146        }
147    
148        protected void setName(IForm form)
149        {
150            form.getElementId(this);
151        }
152    
153        /**
154         * Returns false. Subclasses that might be required must override this method. Typically, this
155         * involves checking against the component's validators.
156         * 
157         * @since 4.0
158         */
159        public boolean isRequired()
160        {
161            return false;
162        }
163    
164        protected abstract void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle);
165    
166        protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle);
167    }