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