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 }