1 /* 2 * $Id: UIForm.java,v 1.54 2008/02/22 01:49:30 rlubke Exp $ 3 */ 4 5 /* 6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 7 * 8 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. 9 * 10 * The contents of this file are subject to the terms of either the GNU 11 * General Public License Version 2 only ("GPL") or the Common Development 12 * and Distribution License("CDDL") (collectively, the "License"). You 13 * may not use this file except in compliance with the License. You can obtain 14 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html 15 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific 16 * language governing permissions and limitations under the License. 17 * 18 * When distributing the software, include this License Header Notice in each 19 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. 20 * Sun designates this particular file as subject to the "Classpath" exception 21 * as provided by Sun in the GPL Version 2 section of the License file that 22 * accompanied this code. If applicable, add the following below the License 23 * Header, with the fields enclosed by brackets [] replaced by your own 24 * identifying information: "Portions Copyrighted [year] 25 * [name of copyright owner]" 26 * 27 * Contributor(s): 28 * 29 * If you wish your version of this file to be governed by only the CDDL or 30 * only the GPL Version 2, indicate your decision by adding "[Contributor] 31 * elects to include this software in this distribution under the [CDDL or GPL 32 * Version 2] license." If you don't indicate a single choice of license, a 33 * recipient has the option to distribute your version of this file under 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to 35 * its licensees as provided above. However, if you add GPL Version 2 code 36 * and therefore, elected the GPL Version 2 license, then the option applies 37 * only if the new code is made subject to such option by the copyright 38 * holder. 39 */ 40 41 package javax.faces.component; 42 43 import java.util.Collection; 44 import java.util.Iterator; 45 import javax.faces.application.Application; 46 import javax.faces.component.visit.VisitCallback; 47 import javax.faces.component.visit.VisitContext; 48 import javax.faces.component.visit.VisitResult; 49 import javax.faces.context.FacesContext; 50 import javax.faces.event.PostValidateEvent; 51 import javax.faces.event.PreValidateEvent; 52 53 54 /** 55 * <p><strong>UIForm</strong> is a {@link UIComponent} that represents an 56 * input form to be presented to the user, and whose child components represent 57 * (among other things) the input fields to be included when the form is 58 * submitted.</p> 59 * <p/> 60 * <p>By default, the <code>rendererType</code> property must be set to 61 * "<code>javax.faces.Form</code>". This value can be changed by calling the 62 * <code>setRendererType()</code> method.</p> 63 */ 64 65 public class UIForm extends UIComponentBase implements NamingContainer, UniqueIdVendor { 66 67 68 // ------------------------------------------------------ Manifest Constants 69 70 71 /** 72 * <p>The standard component type for this component.</p> 73 */ 74 public static final String COMPONENT_TYPE = "javax.faces.Form"; 75 76 77 /** 78 * <p>The standard component family for this component.</p> 79 */ 80 public static final String COMPONENT_FAMILY = "javax.faces.Form"; 81 82 83 /** 84 * Properties that are tracked by state saving. 85 */ 86 enum PropertyKeys { 87 88 /** 89 * <p>The prependId flag.</p> 90 */ 91 prependId, 92 93 /** 94 * <p>Last id vended by {@link UIForm#createUniqueId(javax.faces.context.FacesContext, String)}.</p> 95 */ 96 lastId 97 } 98 99 100 // ------------------------------------------------------------ Constructors 101 102 103 /** 104 * <p>Create a new {@link UIForm} instance with default property 105 * values.</p> 106 */ 107 public UIForm() { 108 109 super(); 110 setRendererType("javax.faces.Form"); 111 112 } 113 114 115 // ------------------------------------------------------ Instance Variables 116 117 //private int lastId = 0; 118 119 // -------------------------------------------------------------- Properties 120 121 122 public String getFamily() { 123 124 return (COMPONENT_FAMILY); 125 126 } 127 128 129 /** 130 * <p>The form submitted flag for this {@link UIForm}.</p> 131 */ 132 private boolean submitted = false; 133 134 135 /** 136 * <p>Returns the current value of the <code>submitted</code> 137 * property. The default value is <code>false</code>. See {@link 138 * #setSubmitted} for details.</p> 139 */ 140 public boolean isSubmitted() { 141 142 return (this.submitted); 143 144 } 145 146 147 /** 148 * <p>If <strong>this</strong> <code>UIForm</code> instance (as 149 * opposed to other forms in the page) is experiencing a submit 150 * during this request processing lifecycle, this method must be 151 * called, with <code>true</code> as the argument, during the {@link 152 * UIComponent#decode} for this <code>UIForm</code> instance. If 153 * <strong>this</strong> <code>UIForm</code> instance is 154 * <strong>not</strong> experiencing a submit, this method must be 155 * called, with <code>false</code> as the argument, during the 156 * {@link UIComponent#decode} for this <code>UIForm</code> 157 * instance.</p> 158 * <p/> 159 * <p>The value of a <code>UIForm</code>'s submitted property must 160 * not be saved as part of its state.</p> 161 */ 162 public void setSubmitted(boolean submitted) { 163 164 this.submitted = submitted; 165 166 } 167 168 /** 169 * <p>The prependId flag.</p> 170 */ 171 //private Boolean prependId; 172 173 174 public boolean isPrependId() { 175 176 return (Boolean) getStateHelper().eval(PropertyKeys.prependId, true); 177 178 } 179 180 181 public void setPrependId(boolean prependId) { 182 183 getStateHelper().put(PropertyKeys.prependId, prependId); 184 185 } 186 187 // ----------------------------------------------------- UIComponent Methods 188 189 190 /** 191 * <p>Override {@link UIComponent#processDecodes} to ensure that the 192 * form is decoded <strong>before</strong> its children. This is 193 * necessary to allow the <code>submitted</code> property to be 194 * correctly set.</p> 195 * 196 * @throws NullPointerException {@inheritDoc} 197 */ 198 public void processDecodes(FacesContext context) { 199 200 if (context == null) { 201 throw new NullPointerException(); 202 } 203 204 // Process this component itself 205 decode(context); 206 207 // if we're not the submitted form, don't process children. 208 if (!isSubmitted()) { 209 return; 210 } 211 212 // Process all facets and children of this component 213 Iterator kids = getFacetsAndChildren(); 214 while (kids.hasNext()) { 215 UIComponent kid = (UIComponent) kids.next(); 216 kid.processDecodes(context); 217 } 218 219 } 220 221 222 /** 223 * <p>Override {@link UIComponent#processValidators} to ensure that 224 * the children of this <code>UIForm</code> instance are only 225 * processed if {@link #isSubmitted} returns <code>true</code>.</p> 226 * 227 * @throws NullPointerException {@inheritDoc} 228 */ 229 public void processValidators(FacesContext context) { 230 231 if (context == null) { 232 throw new NullPointerException(); 233 } 234 if (!isSubmitted()) { 235 return; 236 } 237 Application app = context.getApplication(); 238 app.publishEvent(context, PreValidateEvent.class, this); 239 // Process all the facets and children of this component 240 Iterator kids = getFacetsAndChildren(); 241 while (kids.hasNext()) { 242 UIComponent kid = (UIComponent) kids.next(); 243 kid.processValidators(context); 244 } 245 app.publishEvent(context, PostValidateEvent.class, this); 246 247 } 248 249 250 /** 251 * <p>Override {@link UIComponent#processUpdates} to ensure that the 252 * children of this <code>UIForm</code> instance are only processed 253 * if {@link #isSubmitted} returns <code>true</code>.</p> 254 * 255 * @throws NullPointerException {@inheritDoc} 256 */ 257 public void processUpdates(FacesContext context) { 258 259 if (context == null) { 260 throw new NullPointerException(); 261 } 262 if (!isSubmitted()) { 263 return; 264 } 265 266 // Process all facets and children of this component 267 Iterator kids = getFacetsAndChildren(); 268 while (kids.hasNext()) { 269 UIComponent kid = (UIComponent) kids.next(); 270 kid.processUpdates(context); 271 } 272 273 } 274 275 public String createUniqueId(FacesContext context, String seed) { 276 Integer i = (Integer) getStateHelper().get(PropertyKeys.lastId); 277 int lastId = ((i != null) ? i : 0); 278 getStateHelper().put(PropertyKeys.lastId, ++lastId); 279 return UIViewRoot.UNIQUE_ID_PREFIX + (seed == null ? lastId : seed); 280 } 281 282 /** 283 * <p>Override the {@link UIComponent#getContainerClientId} to allow 284 * users to disable this form from prepending its <code>clientId</code> to 285 * its descendent's <code>clientIds</code> depending on the value of 286 * this form's {@link #isPrependId} property.</p> 287 */ 288 public String getContainerClientId(FacesContext context) { 289 if (this.isPrependId()) { 290 return super.getContainerClientId(context); 291 } else { 292 UIComponent parent = this.getParent(); 293 while (parent != null) { 294 if (parent instanceof NamingContainer) { 295 return parent.getContainerClientId(context); 296 } 297 parent = parent.getParent(); 298 } 299 } 300 return null; 301 } 302 303 304 /** 305 * @see UIComponent#visitTree 306 */ 307 @Override 308 public boolean visitTree(VisitContext context, 309 VisitCallback callback) { 310 311 // NamingContainers can optimize partial tree visits by taking advantage 312 // of the fact that it is possible to detect whether any ids to visit 313 // exist underneath the NamingContainer. If no such ids exist, there 314 // is no need to visit the subtree under the NamingContainer. 315 316 // UIForm is a bit different from other NamingContainers. It only acts 317 // as a NamingContainer when prependId is true. Note that if it 318 // weren't for this, we could push this implementation up in to 319 // UIComponent and share it across all NamingContainers. Instead, 320 // we currently duplicate this implementation in UIForm and 321 // UINamingContainer, so that we can check isPrependId() here. 322 323 if (!this.isPrependId()) { 324 return super.visitTree(context, callback); 325 } 326 327 Collection<String> idsToVisit = context.getSubtreeIdsToVisit(this); 328 assert (idsToVisit != null); 329 330 // If we have ids to visit, let the superclass implementation 331 // handle the visit 332 if (!idsToVisit.isEmpty()) { 333 return super.visitTree(context, callback); 334 } 335 336 // If we have no child ids to visit, just visit ourselves, if 337 // we are visitable. 338 if (isVisitable(context)) { 339 FacesContext facesContext = context.getFacesContext(); 340 pushComponentToEL(facesContext, null); 341 342 try { 343 VisitResult result = context.invokeVisitCallback(this, callback); 344 return (result == VisitResult.COMPLETE); 345 } 346 finally { 347 popComponentFromEL(facesContext); 348 } 349 } 350 351 // Done visiting this subtree. Return false to allow 352 // visit to continue. 353 return false; 354 } 355 } 356