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 protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle); 188 }