1 /* 2 * $Id: UIComponent.java,v 1.153.8.13 2008/04/17 18:51:28 edburns 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 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.util.ArrayList; 47 import java.util.Collections; 48 import java.util.Enumeration; 49 import java.util.HashMap; 50 import java.util.HashSet; 51 import java.util.Iterator; 52 import java.util.List; 53 import java.util.Locale; 54 import java.util.Map; 55 56 import java.util.MissingResourceException; 57 import java.util.PropertyResourceBundle; 58 import java.util.ResourceBundle; 59 import java.util.Set; 60 import java.util.logging.Level; 61 import java.util.logging.Logger; 62 import javax.el.ELContext; 63 import javax.el.ELException; 64 import javax.el.ValueExpression; 65 import javax.faces.FacesException; 66 import javax.faces.FacesWrapper; 67 import javax.faces.application.Resource; 68 import javax.faces.component.visit.VisitCallback; 69 import javax.faces.component.visit.VisitContext; 70 import javax.faces.component.visit.VisitHint; 71 import javax.faces.component.visit.VisitResult; 72 import javax.faces.context.FacesContext; 73 import javax.faces.el.ValueBinding; 74 import javax.faces.event.AbortProcessingException; 75 import javax.faces.event.PostRestoreStateEvent; 76 import javax.faces.event.ComponentSystemEvent; 77 import javax.faces.event.ComponentSystemEventListener; 78 import javax.faces.event.FacesEvent; 79 import javax.faces.event.SystemEvent; 80 import javax.faces.event.SystemEventListener; 81 import javax.faces.event.FacesListener; 82 import javax.faces.event.SystemEventListenerHolder; 83 import javax.faces.render.Renderer; 84 85 /** 86 * <p><strong class="changed_modified_2_0">UIComponent</strong> is the 87 * base class for all user interface components in JavaServer Faces. 88 * The set of {@link UIComponent} instances associated with a particular 89 * request and response are organized into a component tree under a 90 * {@link UIViewRoot} that represents the entire content of the request 91 * or response.</p> 92 * 93 * <p>For the convenience of component developers, 94 * {@link UIComponentBase} provides the default 95 * behavior that is specified for a {@link UIComponent}, and is the base class 96 * for all of the concrete {@link UIComponent} "base" implementations. 97 * Component writers are encouraged to subclass 98 * {@link UIComponentBase}, instead of directly 99 * implementing this abstract class, to reduce the impact of any future changes 100 * to the method signatures.</p> 101 * 102 * <p class="changed_added_2_0">If the {@link 103 * javax.faces.event.ListenerFor} annotation is attached to the class 104 * definition of a <code>Component</code>, that class must also 105 * implement {@link javax.faces.event.ComponentSystemEventListener}. 106 * </p> 107 108 */ 109 110 public abstract class UIComponent implements PartialStateHolder, SystemEventListenerHolder, 111 ComponentSystemEventListener { 112 113 private static Logger LOGGER = Logger.getLogger("javax.faces.component", 114 "javax.faces.LogStrings"); 115 116 /** 117 * <p class="changed_added_2_0">The key to which the 118 * <code>UIComponent</code> currently being processed will be 119 * associated with within the {@link FacesContext} attributes map.</p> 120 * 121 * @see javax.faces.context.FacesContext#getAttributes() 122 * 123 * @since 2.0 124 */ 125 public static final String CURRENT_COMPONENT = "javax.faces.component.CURRENT_COMPONENT"; 126 127 /** 128 * <p class="changed_added_2_0">The key to which the 129 * <em>composite</em> <code>UIComponent</code> currently being 130 * processed will be associated with within the {@link FacesContext} 131 * attributes map.</p> 132 * 133 * @see javax.faces.context.FacesContext#getAttributes() 134 * 135 * @since 2.0 136 */ 137 public static final String CURRENT_COMPOSITE_COMPONENT = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT"; 138 139 /** 140 * <p class="changed_added_2_0">The value of this constant is used as the key in the 141 * component attribute map, the value for which is a 142 * <code>java.beans.BeanInfo</code> implementation describing the composite 143 * component. This <code>BeanInfo</code> is known as the 144 * <em>composite component BeanInfo</em>.</p> 145 * 146 * @since 2.0 147 */ 148 public static final String BEANINFO_KEY = "javax.faces.component.BEANINFO_KEY"; 149 150 151 /** 152 * <p class="changed_added_2_0">The value of this constant is used as the key 153 * in the <em>composite component BeanDescriptor</em> for the 154 * <code>Map<PropertyDescriptor></code> that contains meta-information 155 * for the declared facets for this composite component. 156 * This map must contain an entry under the key {@link #COMPOSITE_FACET_NAME}, even 157 * if no facets were explicitly declared. See {@link #COMPOSITE_FACET_NAME}.</p> 158 * 159 * @since 2.0 160 */ 161 public static final String FACETS_KEY = "javax.faces.component.FACETS_KEY"; 162 163 /** 164 * <p class="changed_added_2_0">The value of this constant is used as the key 165 * in the component attributes <code>Map</code> for the 166 * {@link javax.faces.view.Location} in the view at which this component 167 * instance resides.</p> 168 * 169 * @since 2.0 170 */ 171 public static final String VIEW_LOCATION_KEY = "javax.faces.component.VIEW_LOCATION_KEY"; 172 173 /** 174 * <p class="changed_added_2_0">The value of this constant is used as the key 175 * in the <em>composite component BeanDescriptor</em> for a 176 * <code>ValueExpression</code> that evaluates to the 177 * <code>component-type</code> of the <em>composite component root</em> 178 * <code>UIComponent</code> for this composite component, if 179 * one was declared by the composite component author.</p> 180 * 181 * @since 2.0 182 */ 183 public static final String COMPOSITE_COMPONENT_TYPE_KEY = "javax.faces.component.COMPOSITE_COMPONENT_TYPE"; 184 185 /** 186 * <p class="changed_added_2_0">The value of this constant is used as the key 187 * in the <code>Map</code> returned as described in {@link #FACETS_KEY} 188 * for the 189 * <code>PropertyDescriptor</code> describing the composite component facet. 190 * The value of this constant is also used as the key in the <code>Map</code> 191 * returned from {@link #getFacets}. In this case, it refers to the actual 192 * facet that is the {@link javax.faces.component.UIPanel} that is the parent of the all 193 * of the components in the <code><composite:implementation></code> 194 * section of the <em>composite component VDL file</em>.</p> 195 * 196 * @since 2.0 197 */ 198 public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME"; 199 200 enum PropertyKeysPrivate { 201 attributesThatAreSet 202 } 203 204 /** 205 * Properties that are tracked by state saving. 206 */ 207 enum PropertyKeys { 208 rendered, 209 attributes, 210 bindings, 211 rendererType, 212 systemEventListeners, 213 behaviors 214 } 215 216 /** 217 * List of attributes that have been set on the component (this 218 * may be from setValueExpression, the attributes map, or setters 219 * from the concrete HTML components. This allows 220 * for faster rendering of attributes as this list is authoratative 221 * on what has been set. 222 */ 223 List<String> attributesThatAreSet; 224 StateHelper stateHelper = null; 225 UIComponent compositeParent; 226 227 228 // -------------------------------------------------------------- Attributes 229 230 231 /** 232 * <p>Return a mutable 233 * <code>Map</code> representing the attributes 234 * (and properties, see below) associated wth this {@link UIComponent}, 235 * keyed by attribute name (which must be a String). The returned 236 * implementation must support all of the standard and optional 237 * <code>Map</code> methods, plus support the following additional 238 * requirements:</p> 239 * <ul> 240 * <li>The <code>Map</code> implementation must implement 241 * the <code>java.io.Serializable</code> interface.</li> 242 * <li>Any attempt to add a <code>null</code> key or value must 243 * throw a <code>NullPointerException</code>.</li> 244 * <li>Any attempt to add a key that is not a String must throw 245 * a <code>ClassCastException</code>.</li> 246 * <li>If the attribute name specified as a key matches a property 247 * of this {@link UIComponent}'s implementation class, the following 248 * methods will have special behavior: 249 * <ul> 250 * <li><code>containsKey</code> - Return <code>false</code>.</li> 251 * <li><code>get()</code> - If the property is readable, call 252 * the getter method and return the returned value (wrapping 253 * primitive values in their corresponding wrapper classes); 254 * otherwise throw <code>IllegalArgumentException</code>.</li> 255 * <li><code>put()</code> - If the property is writeable, call 256 * the setter method to set the corresponding value (unwrapping 257 * primitive values in their corresponding wrapper classes). 258 * If the property is not writeable, or an attempt is made to 259 * set a property of primitive type to <code>null</code>, 260 * throw <code>IllegalArgumentException</code>.</li> 261 * <li><code>remove</code> - Throw 262 * <code>IllegalArgumentException</code>.</li> 263 * </ul></li> 264 * </ul> 265 * 266 */ 267 public abstract Map<String, Object> getAttributes(); 268 269 270 // ---------------------------------------------------------------- Bindings 271 272 273 /** 274 * 275 * <p>Call through to {@link #getValueExpression} and examine the 276 * result. If the result is an instance of the wrapper class 277 * mandated in {@link #setValueBinding}, extract the 278 * <code>ValueBinding</code> instance and return it. Otherwise, 279 * wrap the result in an implementation of 280 * <code>ValueBinding</code>, and return it.</p> 281 * 282 * @param name Name of the attribute or property for which to retrieve a 283 * {@link ValueBinding} 284 * 285 * @throws NullPointerException if <code>name</code> 286 * is <code>null</code> 287 * 288 * @deprecated This has been replaced by {@link #getValueExpression}. 289 */ 290 public abstract ValueBinding getValueBinding(String name); 291 292 293 /** 294 * <p>Wrap the argument <code>binding</code> in an implementation of 295 * {@link ValueExpression} and call through to {@link 296 * #setValueExpression}.</p> 297 * 298 * @param name Name of the attribute or property for which to set a 299 * {@link ValueBinding} 300 * @param binding The {@link ValueBinding} to set, or <code>null</code> 301 * to remove any currently set {@link ValueBinding} 302 * 303 * @throws IllegalArgumentException if <code>name</code> is one of 304 * <code>id</code> or <code>parent</code> 305 * @throws NullPointerException if <code>name</code> 306 * is <code>null</code> 307 * 308 * @deprecated This has been replaced by {@link #setValueExpression}. 309 */ 310 public abstract void setValueBinding(String name, ValueBinding binding); 311 312 // The set of ValueExpressions for this component, keyed by property 313 // name This collection is lazily instantiated 314 // The set of ValueExpressions for this component, keyed by property 315 // name This collection is lazily instantiated 316 @Deprecated 317 protected Map<String,ValueExpression> bindings = null; 318 319 /** 320 * <p>Return the {@link ValueExpression} used to calculate the value for the 321 * specified attribute or property name, if any.</p> 322 * 323 * <p>This method must be overridden and implemented for components that 324 * comply with JSF 1.2 and later.</p> 325 * 326 * @since 1.2 327 * 328 * @param name Name of the attribute or property for which to retrieve a 329 * {@link ValueExpression} 330 * 331 * @throws NullPointerException if <code>name</code> 332 * is <code>null</code> 333 * 334 */ 335 public ValueExpression getValueExpression(String name) { 336 337 if (name == null) { 338 throw new NullPointerException(); 339 } 340 341 Map<String,ValueExpression> map = (Map<String,ValueExpression>) 342 getStateHelper().get(UIComponentBase.PropertyKeys.bindings); 343 return ((map != null) ? map.get(name) : null); 344 345 } 346 347 /** 348 * <p>Set the {@link ValueExpression} used to calculate the value 349 * for the specified attribute or property name, if any.</p> 350 * 351 * <p>The implementation must call {@link 352 * ValueExpression#isLiteralText} on the argument 353 * <code>expression</code>. If <code>isLiteralText()</code> returns 354 * <code>true</code>, invoke {@link ValueExpression#getValue} on the 355 * argument expression and pass the result as the <code>value</code> 356 * parameter in a call to <code>this.{@link 357 * #getAttributes()}.put(name, value)</code> where <code>name</code> 358 * is the argument <code>name</code>. If an exception is thrown as 359 * a result of calling {@link ValueExpression#getValue}, wrap it in 360 * a {@link javax.faces.FacesException} and re-throw it. If 361 * <code>isLiteralText()</code> returns <code>false</code>, simply 362 * store the un-evaluated <code>expression</code> argument in the 363 * collection of <code>ValueExpression</code>s under the key given 364 * by the argument <code>name</code>.</p> 365 * 366 * <p>This method must be overridden and implemented for components that 367 * comply with JSF 1.2 and later.</p> 368 * 369 * @since 1.2 370 * 371 * @param name Name of the attribute or property for which to set a 372 * {@link ValueExpression} 373 * @param binding The {@link ValueExpression} to set, or <code>null</code> 374 * to remove any currently set {@link ValueExpression} 375 * 376 * @throws IllegalArgumentException if <code>name</code> is one of 377 * <code>id</code> or <code>parent</code> 378 * @throws NullPointerException if <code>name</code> 379 * is <code>null</code> 380 * 381 */ 382 public void setValueExpression(String name, ValueExpression binding) { 383 384 if (name == null) { 385 throw new NullPointerException(); 386 } else if ("id".equals(name) || "parent".equals(name)) { 387 throw new IllegalArgumentException(); 388 } 389 390 if (binding != null) { 391 if (!binding.isLiteralText()) { 392 //if (bindings == null) { 393 // //noinspection CollectionWithoutInitialCapacity 394 // bindings = new HashMap<String, ValueExpression>(); 395 //} 396 // add this binding name to the 'attributesThatAreSet' list 397 //List<String> sProperties = (List<String>) 398 // getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet); 399 400 List<String> sProperties = 401 (List<String>) getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet); 402 if (sProperties == null) { 403 getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, name); 404 } else if (!sProperties.contains(name)) { 405 getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, name); 406 } 407 getStateHelper().put(UIComponentBase.PropertyKeys.bindings, 408 name, 409 binding); 410 //bindings.put(name, binding); 411 } else { 412 ELContext context = 413 FacesContext.getCurrentInstance().getELContext(); 414 try { 415 getAttributes().put(name, binding.getValue(context)); 416 } catch (ELException ele) { 417 throw new FacesException(ele); 418 } 419 } 420 } else { 421 //if (bindings != null) { 422 // remove this binding name from the 'attributesThatAreSet' list 423 // List<String> sProperties = getAttributesThatAreSet(false); 424 // if (sProperties != null) { 425 // sProperties.remove(name); 426 // } 427 getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet, 428 name); 429 getStateHelper().remove(UIComponentBase.PropertyKeys.bindings, name); 430 //bindings.remove(name); 431 // if (bindings.isEmpty()) { 432 // bindings = null; 433 // } 434 } 435 // } 436 437 } 438 439 // -------------------------------------------------------------- Properties 440 441 boolean initialState; 442 443 /** 444 * <p class="changed_added_2_0">An implementation of {@link 445 * PartialStateHolder#markInitialState}, this method is called by 446 * the runtime to indicate that the instance should start tracking 447 * changes to its state.</p> 448 * @since 2.0 449 */ 450 public void markInitialState() { 451 initialState = true; 452 } 453 454 455 /** 456 * <p class="changed_added_2_0">An implementation of {@link 457 * PartialStateHolder#initialStateMarked}, this method is called by 458 * the runtime to test if the {@link 459 * PartialStateHolder#markInitialState} method was called.</p> 460 * @since 2.0 461 */ 462 public boolean initialStateMarked() { 463 return initialState; 464 } 465 466 467 /** 468 * <p class="changed_added_2_0">An implementation of {@link 469 * PartialStateHolder#clearInitialState}, this method is called by 470 * the runtime to tell the instance to stop tracking state 471 * changes.</p> 472 * @since 2.0 473 */ 474 public void clearInitialState() { 475 initialState = false; 476 } 477 478 479 /** 480 * <p class="changed_added_2_0">Return the {@link StateHelper} 481 * instance used to help this component implement {@link 482 * PartialStateHolder}.</p> 483 * @since 2.0 484 */ 485 protected StateHelper getStateHelper() { 486 return getStateHelper(true); 487 } 488 489 490 /** 491 * <p class="changed_added_2_0">Like {@link #getStateHelper()}, but 492 * only create a state helper instance if the argument 493 * <code>creat</code> is <code>true</code>.</p> 494 * @param create if <code>true</code>, a new {@link StateHelper} 495 * instance will be created if it does not exist already. If 496 * <code>false</code>, and there is no existing 497 * <code>StateHelper</code> instance, one will not be created and 498 * <code>null</code> will be returned. 499 * @since 2.0 500 */ 501 protected StateHelper getStateHelper(boolean create) { 502 503 if (create && stateHelper == null) { 504 stateHelper = new ComponentStateHelper(this); 505 } 506 return stateHelper; 507 508 } 509 510 511 private boolean isInView; 512 513 514 /** 515 * <p class="changed_added_2_0">Return <code>true</code> if this 516 * component is within the view hierarchy otherwise 517 * <code>false</code></code> 518 * 519 * @since 2.0 520 */ 521 public boolean isInView() { 522 return isInView; 523 } 524 525 526 /** 527 * <p class="changed_added_2_0">Updates the status as to whether or 528 * not this component is currently within the view hierarchy. 529 * <strong>This method must never be called by developers; a {@link 530 * UIComponent}'s internal implementation will call it as components 531 * are added to or removed from a parent's child <code>List</code> 532 * or facet <code>Map</code></strong>.</p> 533 * 534 * @param isInView flag indicating whether or not this component is within 535 * the view hierachy 536 * 537 * @since 2.0 538 */ 539 public void setInView(boolean isInView) { 540 this.isInView = isInView; 541 } 542 543 544 /** 545 * <p class="changed_added_2_0">Enable EL to access the <code>clientId</code> 546 * of a component. This is particularly useful in combination with the 547 * <code>component</code> and <code>cc</code> implicit 548 * objects. A default implementation is provided that simply calls 549 * {@link FacesContext#getCurrentInstance} and then calls through to 550 * {@link #getClientId(FacesContext)}.</p> 551 * 552 * @since 2.0 553 */ 554 555 public String getClientId() { 556 FacesContext context = FacesContext.getCurrentInstance(); 557 return getClientId(context); 558 } 559 560 561 /** 562 * <p>Return a client-side identifier for this component, generating 563 * one if necessary. The associated {@link Renderer}, if any, 564 * will be asked to convert the clientId to a form suitable for 565 * transmission to the client.</p> 566 * 567 * <p>The return from this method must be the same value throughout 568 * the lifetime of the instance, unless the <code>id</code> property 569 * of the component is changed, or the component is placed in 570 * a {@link NamingContainer} whose client ID changes (for example, 571 * {@link UIData}). However, even in these cases, consecutive 572 * calls to this method must always return the same value. The 573 * implementation must follow these steps in determining the 574 * clientId:</p> 575 * 576 * <p>Find the closest ancestor to <b>this</b> component in the view 577 * hierarchy that implements <code>NamingContainer</code>. Call 578 * <code>getContainerClientId()</code> on it and save the result as 579 * the <code>parentId</code> local variable. Call {@link #getId} on 580 * <b>this</b> component and save the result as the 581 * <code>myId</code> local variable. If <code>myId</code> is 582 * <code>null</code>, call 583 * <code>context.getViewRoot().createUniqueId()</code> and assign 584 * the result to myId. If <code>parentId</code> is 585 * non-<code>null</code>, let <code>myId</code> equal <code>parentId 586 * + {@link UINamingContainer#getSeparatorChar} + myId</code>. Call 587 * {@link Renderer#convertClientId}, passing <code>myId</code>, and 588 * return the result.</p> 589 * 590 * @param context The {@link FacesContext} for the current request 591 * 592 * @throws NullPointerException if <code>context</code> 593 * is <code>null</code> 594 */ 595 public abstract String getClientId(FacesContext context); 596 597 /** 598 * <p>Allow components that implement {@link NamingContainer} to 599 * selectively disable prepending their clientId to their 600 * descendent's clientIds by breaking the prepending logic into a 601 * seperately callable method. See {@link #getClientId} for usage.</p> 602 * 603 * <p>By default, this method will call through to {@link 604 * #getClientId} and return the result. 605 * 606 * @since 1.2 607 * 608 * @throws NullPointerException if <code>context</code> is 609 * <code>null</code> 610 */ 611 public String getContainerClientId(FacesContext context) { 612 if (context == null) { 613 throw new NullPointerException(); 614 } 615 return this.getClientId(context); 616 } 617 618 /** 619 * <p>Return the identifier of the component family to which this 620 * component belongs. This identifier, in conjunction with the value 621 * of the <code>rendererType</code> property, may be used to select 622 * the appropriate {@link Renderer} for this component instance.</p> 623 */ 624 public abstract String getFamily(); 625 626 627 /** 628 * <p>Return the component identifier of this {@link UIComponent}.</p> 629 */ 630 public abstract String getId(); 631 632 633 /** 634 * <p>Set the component identifier of this {@link UIComponent} (if any). 635 * Component identifiers must obey the following syntax restrictions:</p> 636 * <ul> 637 * <li>Must not be a zero-length String.</li> 638 * <li>First character must be a letter or an underscore ('_').</li> 639 * <li>Subsequent characters must be a letter, a digit, 640 * an underscore ('_'), or a dash ('-').</li> 641 * <li> 642 * </ul> 643 * 644 * <p>Component identifiers must also obey the following semantic 645 * restrictions (note that this restriction is <strong>NOT</strong> 646 * enforced by the <code>setId()</code> implementation):</p> 647 * <ul> 648 * <li>The specified identifier must be unique among all the components 649 * (including facets) that are descendents of the nearest ancestor 650 * {@link UIComponent} that is a {@link NamingContainer}, or within 651 * the scope of the entire component tree if there is no such 652 * ancestor that is a {@link NamingContainer}.</li> 653 * </ul> 654 * 655 * @param id The new component identifier, or <code>null</code> to indicate 656 * that this {@link UIComponent} does not have a component identifier 657 * 658 * @throws IllegalArgumentException if <code>id</code> is not 659 * syntactically valid 660 */ 661 public abstract void setId(String id); 662 663 664 /** 665 * <p>Return the parent {@link UIComponent} of this 666 * <code>UIComponent</code>, if any. A component must allow child 667 * components to be added to and removed from the list of children 668 * of this component, even though the child component returns null 669 * from <code>getParent( )</code>.</p> 670 */ 671 public abstract UIComponent getParent(); 672 673 674 /** 675 * <p class="changed_modified_2_0">Set the parent 676 * <code>UIComponent</code> of this <code>UIComponent</code>. <span 677 * class="changed_added_2_0">This method will cause an {@link 678 * javax.faces.event.PostAddToViewEvent} to be published and if 679 * <code>parent.isInView()</code> returns <code>true</code> an 680 * {@link javax.faces.event.PostAddToViewEvent} will be published as 681 * well. <strong>This method must never be called by developers; a 682 * {@link UIComponent}'s internal implementation will call it as 683 * components are added to or removed from a parent's child 684 * <code>List</code> or facet <code>Map</code></strong></span>.</p> 685 * 686 * @param parent The new parent, or <code>null</code> for the root node 687 * of a component tree 688 */ 689 public abstract void setParent(UIComponent parent); 690 691 692 /** 693 * <p>Return <code>true</code> if this component (and its children) 694 * should be rendered during the <em>Render Response</em> phase 695 * of the request processing lifecycle.</p> 696 */ 697 public abstract boolean isRendered(); 698 699 700 /** 701 * <p>Set the <code>rendered</code> property of this 702 * {@link UIComponent}.</p> 703 * 704 * @param rendered If <code>true</code> render this component; 705 * otherwise, do not render this component 706 */ 707 public abstract void setRendered(boolean rendered); 708 709 710 /** 711 * <p>Return the {@link Renderer} type for this {@link UIComponent} 712 * (if any).</p> 713 */ 714 public abstract String getRendererType(); 715 716 717 /** 718 * <p>Set the {@link Renderer} type for this {@link UIComponent}, 719 * or <code>null</code> for components that render themselves.</p> 720 * 721 * @param rendererType Logical identifier of the type of 722 * {@link Renderer} to use, or <code>null</code> for components 723 * that render themselves 724 */ 725 public abstract void setRendererType(String rendererType); 726 727 728 /** 729 * <p>Return a flag indicating whether this component is responsible 730 * for rendering its child components. The default implementation 731 * in {@link UIComponentBase#getRendersChildren} tries to find the 732 * renderer for this component. If it does, it calls {@link 733 * Renderer#getRendersChildren} and returns the result. If it 734 * doesn't, it returns false. As of version 1.2 of the JavaServer 735 * Faces Specification, component authors are encouraged to return 736 * <code>true</code> from this method and rely on {@link 737 * UIComponentBase#encodeChildren}.</p> 738 */ 739 public abstract boolean getRendersChildren(); 740 741 742 743 private Map<String, String> resourceBundleMap = null; 744 745 /** 746 * <p class="changed_added_2_0">Return a 747 * <code>Map<String,String></code> of the 748 * <code>ResourceBundle</code> for this component. A component may 749 * have a <code>ResourceBundle</code> associated with it. This 750 * bundle may contain localized properties relating to instances of 751 * this component. The default implementation first looks for a 752 * <code>ResourceBundle</code> with a base name equal to the fully 753 * qualified class name of the current <code>UIComponent this</code> 754 * and <code>Locale</code> equal to the <code>Locale</code> of the 755 * current <code>UIViewRoot</code>. If no such bundle is found, and 756 * the component is a composite component, let <em>resourceName</em> 757 * be the <em>resourceName</em> of the {@link Resource} for this 758 * composite component, replacing the file extension with 759 * ".properties". Let <em>libraryName</em> be the 760 * <em>libraryName</em> of the the {@link Resource} for this 761 * composite component. Call {@link 762 * javax.faces.application.ResourceHandler#createResource(java.lang.String,java.lang.String)}, 763 * passing the derived <em>resourceName</em> and 764 * <em>libraryName</em>. Note that this will automatically allow 765 * for the localization of the <code>ResourceBundle</code> due to 766 * the localization facility implemented in 767 * <code>createResource</code>, which is specified in section 768 * JSF.2.6.1.3 of the spec prose document. If the resultant {@link 769 * Resource} exists and can be found, the <code>InputStream</code> 770 * for the resource is used to create a <code>ResourceBundle</code>. 771 * If either of the two previous steps for obtaining the 772 * <code>ResourceBundle</code> for this component is successful, the 773 * <code>ResourceBundle</code> is wrapped in a 774 * <code>Map<String,String></code> and returned. Otherwise 775 * <code>Collections.EMPTY_MAP</code> is returned.</p> 776 * 777 * @since 2.0 778 */ 779 public Map<String,String> getResourceBundleMap() { 780 781 if (null == resourceBundleMap) { 782 // See if there is a ResourceBundle under the FQCN for this class 783 String className = this.getClass().getName(); 784 Locale currentLocale = null; 785 FacesContext context = null; 786 UIViewRoot root = null; 787 ResourceBundle resourceBundle = null; 788 789 // Step 1: look for a ResourceBundle under the FQCN of this instance 790 if (null != (context = FacesContext.getCurrentInstance())) { 791 if (null != (root = context.getViewRoot())) { 792 currentLocale = root.getLocale(); 793 } 794 } 795 if (null == currentLocale) { 796 currentLocale = Locale.getDefault(); 797 } 798 try { 799 resourceBundle = 800 ResourceBundle.getBundle(className, currentLocale); 801 } catch (MissingResourceException e) { 802 // It is not an error if there is no ResourceBundle 803 } 804 805 // Step 2: if this is a composite component, look for a 806 // ResourceBundle as a Resource 807 if (null == resourceBundle) { 808 if (this.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)) { 809 Resource ccResource = (Resource) 810 this.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY); 811 if (null != ccResource) { 812 if (null != (ccResource = 813 findComponentResourceBundleLocaleMatch(context, 814 ccResource.getResourceName(), 815 ccResource.getLibraryName()))) { 816 try { 817 InputStream propertiesInputStream = ccResource.getInputStream(); 818 resourceBundle = new PropertyResourceBundle(propertiesInputStream); 819 } catch (IOException ex) { 820 Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex); 821 } 822 } 823 } 824 } 825 } 826 827 // Step 3: if the previous steps yielded a ResourceBundle, wrap it 828 // with a Map 829 830 if (null != resourceBundle) { 831 final ResourceBundle bundle = resourceBundle; 832 resourceBundleMap = 833 new Map() { 834 // this is an immutable Map 835 836 public String toString() { 837 StringBuffer sb = new StringBuffer(); 838 Iterator<Map.Entry<String, Object>> entries = 839 this.entrySet().iterator(); 840 Map.Entry<String, Object> cur; 841 while (entries.hasNext()) { 842 cur = entries.next(); 843 sb.append(cur.getKey()).append(": ").append(cur.getValue()).append('\n'); 844 } 845 846 return sb.toString(); 847 } 848 849 // Do not need to implement for immutable Map 850 public void clear() { 851 throw new UnsupportedOperationException(); 852 } 853 854 855 public boolean containsKey(Object key) { 856 boolean result = false; 857 if (null != key) { 858 result = (null != bundle.getObject(key.toString())); 859 } 860 return result; 861 } 862 863 864 public boolean containsValue(Object value) { 865 Enumeration<String> keys = bundle.getKeys(); 866 boolean result = false; 867 while (keys.hasMoreElements()) { 868 Object curObj = bundle.getObject(keys.nextElement()); 869 if ((curObj == value) || 870 ((null != curObj) && curObj.equals(value))) { 871 result = true; 872 break; 873 } 874 } 875 return result; 876 } 877 878 879 public Set<Map.Entry<String, Object>> entrySet() { 880 HashMap<String, Object> mappings = new HashMap<String, Object>(); 881 Enumeration<String> keys = bundle.getKeys(); 882 while (keys.hasMoreElements()) { 883 String key = keys.nextElement(); 884 Object value = bundle.getObject(key); 885 mappings.put(key, value); 886 } 887 return mappings.entrySet(); 888 } 889 890 891 @Override 892 public boolean equals(Object obj) { 893 return !((obj == null) || !(obj instanceof Map)) 894 && entrySet().equals(((Map) obj).entrySet()); 895 896 } 897 898 899 public Object get(Object key) { 900 if (null == key) { 901 return null; 902 } 903 try { 904 return bundle.getObject(key.toString()); 905 } catch (MissingResourceException e) { 906 return "???" + key + "???"; 907 } 908 } 909 910 911 public int hashCode() { 912 return bundle.hashCode(); 913 } 914 915 916 public boolean isEmpty() { 917 Enumeration<String> keys = bundle.getKeys(); 918 return !keys.hasMoreElements(); 919 } 920 921 922 public Set keySet() { 923 Set<String> keySet = new HashSet<String>(); 924 Enumeration<String> keys = bundle.getKeys(); 925 while (keys.hasMoreElements()) { 926 keySet.add(keys.nextElement()); 927 } 928 return keySet; 929 } 930 931 932 // Do not need to implement for immutable Map 933 public Object put(Object k, Object v) { 934 throw new UnsupportedOperationException(); 935 } 936 937 938 // Do not need to implement for immutable Map 939 public void putAll(Map t) { 940 throw new UnsupportedOperationException(); 941 } 942 943 944 // Do not need to implement for immutable Map 945 public Object remove(Object k) { 946 throw new UnsupportedOperationException(); 947 } 948 949 950 public int size() { 951 int result = 0; 952 Enumeration<String> keys = bundle.getKeys(); 953 while (keys.hasMoreElements()) { 954 keys.nextElement(); 955 result++; 956 } 957 return result; 958 } 959 960 961 public java.util.Collection values() { 962 ArrayList<Object> result = new ArrayList<Object>(); 963 Enumeration<String> keys = bundle.getKeys(); 964 while (keys.hasMoreElements()) { 965 result.add( 966 bundle.getObject(keys.nextElement())); 967 } 968 return result; 969 } 970 }; 971 972 } 973 974 if (null == resourceBundleMap) { 975 resourceBundleMap = Collections.EMPTY_MAP; 976 } 977 978 } 979 980 return resourceBundleMap; 981 } 982 983 // PENDING(rlubke): I'm sure there's a more efficient 984 // way to handle this. 985 private Resource findComponentResourceBundleLocaleMatch(FacesContext context, 986 String resourceName, String libraryName) { 987 Resource result = null; 988 ResourceBundle resourceBundle = null; 989 int i; 990 if (-1 != (i = resourceName.lastIndexOf("."))) { 991 resourceName = resourceName.substring(0, i) + 992 ".properties"; 993 if (null != context) { 994 result = context.getApplication().getResourceHandler(). 995 createResource(resourceName, libraryName); 996 try { 997 InputStream propertiesInputStream = result.getInputStream(); 998 resourceBundle = new PropertyResourceBundle(propertiesInputStream); 999 } catch (IOException ex) { 1000 Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex); 1001 } 1002 } 1003 } 1004 result = (null != resourceBundle) ? result : null; 1005 1006 return result; 1007 } 1008 1009 1010 // This is necessary for JSF components that extend from UIComponent 1011 // directly rather than extending from UIComponentBase. Such components 1012 // may need to have implementations provided for methods that originated 1013 // from a spec version more recent than the version with which the component 1014 // complies. Currently this private property is only consulted in the 1015 // getValueExpression() method. 1016 private boolean isUIComponentBase; 1017 private boolean isUIComponentBaseIsSet = false; 1018 1019 private boolean isUIComponentBase() { 1020 if (!isUIComponentBaseIsSet) { 1021 isUIComponentBase = (this instanceof UIComponentBase); 1022 } 1023 1024 return isUIComponentBase; 1025 } 1026 1027 1028 // ------------------------------------------------- Tree Management Methods 1029 1030 1031 /** 1032 * <p><span class="changed_modified_2_0">Return</span> a mutable 1033 * <code>List</code> representing the child {@link UIComponent}s 1034 * associated with this component. The returned implementation must 1035 * support all of the standard and optional <code>List</code> 1036 * methods, plus support the following additional requirements:</p> 1037 * <ul> <li>The <code>List</code> implementation must implement the 1038 * <code>java.io.Serializable</code> interface.</li> <li>Any attempt 1039 * to add a <code>null</code> must throw a NullPointerException</li> 1040 * <li>Any attempt to add an object that does not implement {@link 1041 * UIComponent} must throw a ClassCastException.</li> <li>Whenever a 1042 * new child component is added, the <code>parent</code> property of 1043 * the child must be set to this component instance. If the 1044 * <code>parent</code> property of the child was already non-null, 1045 * the child must first be removed from its previous parent (where 1046 * it may have been either a child or a facet).</li> <li>Whenever an 1047 * existing child component is removed, the <code>parent</code> 1048 * property of the child must be set to <code>null</code>.</li> 1049 1050 * <li class="changed_added_2_0"><p>After the child component has 1051 * been added to the view, if the following condition is 1052 * <strong>not</strong> met:</p> 1053 * 1054 * <ul><p>{@link javax.faces.context.FacesContext#isPostback} 1055 * returns <code>true</code> and {@link 1056 * javax.faces.context.FacesContext#getCurrentPhaseId} returns {@link 1057 * javax.faces.event.PhaseId#RESTORE_VIEW}</p></ul> 1058 1059 * <p>{@link javax.faces.application.Application#publishEvent} 1060 * must be called, passing {@link 1061 * javax.faces.event.PostAddToViewEvent}<code>.class</code> 1062 * as the first argument and the newly added component as the 1063 * second argument.</p> 1064 1065 * </li> 1066 1067 * </ul> 1068 */ 1069 public abstract List<UIComponent> getChildren(); 1070 1071 1072 /** 1073 * <p>Return the number of child {@link UIComponent}s that are 1074 * associated with this {@link UIComponent}. If there are no 1075 * children, this method must return 0. The method must not cause 1076 * the creation of a child component list.</p> 1077 */ 1078 public abstract int getChildCount(); 1079 1080 1081 /** 1082 * <p>Search for and return the {@link UIComponent} with an <code>id</code> 1083 * that matches the specified search expression (if any), according to the 1084 * algorithm described below.</p> 1085 * 1086 * <p>For a method to find a component given a simple 1087 * <code>clientId</code>, see {@link #invokeOnComponent}.</p> 1088 * 1089 * <p>Component identifiers are required to be unique within the scope of 1090 * the closest ancestor {@link NamingContainer} that encloses this 1091 * component (which might be this component itself). If there are no 1092 * {@link NamingContainer} components in the ancestry of this component, 1093 * the root component in the tree is treated as if it were a 1094 * {@link NamingContainer}, whether or not its class actually implements 1095 * the {@link NamingContainer} interface.</p> 1096 * 1097 * <p>A <em>search expression</em> consists of either an identifier 1098 * (which is matched exactly against the <code>id</code> property of 1099 * a {@link UIComponent}, or a series of such identifiers linked by 1100 * the {@link UINamingContainer#getSeparatorChar} character value. 1101 * The search algorithm should operates as follows, though alternate 1102 * alogrithms may be used as long as the end result is the same:</p> 1103 1104 * <ul> 1105 * <li>Identify the {@link UIComponent} that will be the base for searching, 1106 * by stopping as soon as one of the following conditions is met: 1107 * <ul> 1108 * <li>If the search expression begins with the the separator character 1109 * (called an "absolute" search expression), 1110 * the base will be the root {@link UIComponent} of the component 1111 * tree. The leading separator character will be stripped off, 1112 * and the remainder of the search expression will be treated as 1113 * a "relative" search expression as described below.</li> 1114 * <li>Otherwise, if this {@link UIComponent} is a 1115 * {@link NamingContainer} it will serve as the basis.</li> 1116 * <li>Otherwise, search up the parents of this component. If 1117 * a {@link NamingContainer} is encountered, it will be the base. 1118 * </li> 1119 * <li>Otherwise (if no {@link NamingContainer} is encountered) 1120 * the root {@link UIComponent} will be the base.</li> 1121 * </ul></li> 1122 * <li>The search expression (possibly modified in the previous step) is now 1123 * a "relative" search expression that will be used to locate the 1124 * component (if any) that has an <code>id</code> that matches, within 1125 * the scope of the base component. The match is performed as follows: 1126 * <ul> 1127 * <li>If the search expression is a simple identifier, this value is 1128 * compared to the <code>id</code> property, and then recursively 1129 * through the facets and children of the base {@link UIComponent} 1130 * (except that if a descendant {@link NamingContainer} is found, 1131 * its own facets and children are not searched).</li> 1132 * <li>If the search expression includes more than one identifier 1133 * separated by the separator character, the first identifier is 1134 * used to locate a {@link NamingContainer} by the rules in the 1135 * previous bullet point. Then, the <code>findComponent()</code> 1136 * method of this {@link NamingContainer} will be called, passing 1137 * the remainder of the search expression.</li> 1138 * </ul></li> 1139 * </ul> 1140 * 1141 * @param expr Search expression identifying the {@link UIComponent} 1142 * to be returned 1143 * 1144 * @return the found {@link UIComponent}, or <code>null</code> 1145 * if the component was not found. 1146 * 1147 * @throws IllegalArgumentException if an intermediate identifier 1148 * in a search expression identifies a {@link UIComponent} that is 1149 * not a {@link NamingContainer} 1150 * @throws NullPointerException if <code>expr</code> 1151 * is <code>null</code> 1152 */ 1153 public abstract UIComponent findComponent(String expr); 1154 1155 /** 1156 * <p>Starting at this component in the View hierarchy, search for a 1157 * component with a <code>clientId</code> equal to the argument 1158 * <code>clientId</code> and, if found, call the {@link 1159 * ContextCallback#invokeContextCallback} method on the argument 1160 * <code>callback</code>, passing the current {@link FacesContext} 1161 * and the found component as arguments. This method is similar to 1162 * {@link #findComponent} but it does not support the leading {@link 1163 * UINamingContainer#getSeparatorChar} syntax for searching from the 1164 * root of the View.</p> 1165 * 1166 * <p>The default implementation will first check if 1167 * <code>this.getClientId()</code> is equal to the argument 1168 * <code>clientId</code>. If so, call the {@link 1169 * ContextCallback#invokeContextCallback} method on the argument callback, 1170 * passing through the <code>FacesContext</code> argument and 1171 * passing this as the component argument. If an 1172 * <code>Exception</code> is thrown by the callback, wrap it in a 1173 * {@link FacesException} and re-throw it. Otherwise, return 1174 * <code>true</code>.</p> 1175 * 1176 * <p>Otherwise, for each component returned by {@link 1177 * #getFacetsAndChildren}, call <code>invokeOnComponent()</code> 1178 * passing the arguments to this method, in order. The first time 1179 * <code>invokeOnComponent()</code> returns true, abort traversing 1180 * the rest of the <code>Iterator</code> and return 1181 * <code>true</code>.</p> 1182 * 1183 * <p>When calling {@link ContextCallback#invokeContextCallback} 1184 * the implementation of this method must guarantee that the state 1185 * of the component passed to the callback correctly reflects the 1186 * component's position in the View hierarchy with respect to any 1187 * state found in the argument <code>clientId</code>. For example, 1188 * an iterating component such as {@link UIData} will need to set 1189 * its row index to correctly reflect the argument 1190 * <code>clientId</code> before finding the appropriate child 1191 * component backed by the correct row. When the callback returns, 1192 * either normally or by throwing an <code>Exception</code> the 1193 * implementation of this method must restore the state of the view 1194 * to the way it was before invoking the callback.</p> 1195 * 1196 * <p>If none of the elements from {@link 1197 * #getFacetsAndChildren} returned <code>true</code> from 1198 * <code>invokeOnComponent()</code>, return <code>false</code>.</p> 1199 * 1200 * <p>Simple usage example to find a component by 1201 * <code>clientId</code>.</p> 1202 1203 * <pre><code> 1204 private UIComponent found = null; 1205 1206 private void doFind(FacesContext context, String clientId) { 1207 context.getViewRoot().invokeOnComponent(context, clientId, 1208 new ContextCallback() { 1209 public void invokeContextCallback(FacesContext context, 1210 UIComponent component) { 1211 found = component; 1212 } 1213 }); 1214 } 1215 * </code></pre> 1216 1217 * 1218 * 1219 * @since 1.2 1220 * 1221 * @param context the {@link FacesContext} for the current request 1222 * 1223 * @param clientId the client identifier of the component to be passed 1224 * to the argument callback. 1225 * 1226 * @param callback an implementation of the Callback interface. 1227 * 1228 * @throws NullPointerException if any of the arguments are null 1229 * 1230 * @throws FacesException if the argument Callback throws an 1231 * Exception, it is wrapped in a <code>FacesException</code> and re-thrown. 1232 * 1233 * @return <code>true</code> if the a component with the given 1234 * <code>clientId</code> is found, the callback method was 1235 * successfully invoked passing that component as an argument, and 1236 * no Exception was thrown. Returns <code>false</code> if no 1237 * component with the given <code>clientId</code> is found. 1238 * 1239 */ 1240 1241 public boolean invokeOnComponent(FacesContext context, String clientId, 1242 ContextCallback callback) throws FacesException { 1243 if (null == context || null == clientId || null == callback) { 1244 throw new NullPointerException(); 1245 } 1246 1247 boolean found = false; 1248 if (clientId.equals(this.getClientId(context))) { 1249 try { 1250 callback.invokeContextCallback(context, this); 1251 return true; 1252 } catch (Exception e) { 1253 throw new FacesException(e); 1254 } 1255 } else { 1256 Iterator<UIComponent> itr = this.getFacetsAndChildren(); 1257 1258 while (itr.hasNext() && !found) { 1259 found = itr.next().invokeOnComponent(context, clientId, 1260 callback); 1261 } 1262 } 1263 return found; 1264 } 1265 1266 // ------------------------------------------------ Facet Management Methods 1267 1268 1269 /** 1270 * <p>Return a mutable <code>Map</code> representing the facet 1271 * {@link UIComponent}s associated with this {@link UIComponent}, 1272 * keyed by facet name (which must be a String). The returned 1273 * implementation must support all of the standard and optional 1274 * <code>Map</code> methods, plus support the following additional 1275 * requirements:</p> 1276 1277 * <ul> 1278 * <li>The <code>Map</code> implementation must implement 1279 * the <code>java.io.Serializable</code> interface.</li> 1280 * <li>Any attempt to add a <code>null</code> key or value must 1281 * throw a NullPointerException.</li> 1282 * <li>Any attempt to add a key that is not a String must throw 1283 * a ClassCastException.</li> 1284 * <li>Any attempt to add a value that is not a {@link UIComponent} 1285 * must throw a ClassCastException.</li> 1286 * <li>Whenever a new facet {@link UIComponent} is added: 1287 * <ul> 1288 * <li>The <code>parent</code> property of the component must be set to 1289 * this component instance.</li> 1290 * <li>If the <code>parent</code> property of the component was already 1291 * non-null, the component must first be removed from its previous 1292 * parent (where it may have been either a child or a facet).</li> 1293 * </ul></li> 1294 1295 * <li>Whenever an existing facet {@link UIComponent} is removed: 1296 * <ul> 1297 * <li>The <code>parent</code> property of the facet must be 1298 * set to <code>null</code>.</li> 1299 * </ul></li> 1300 * </ul> 1301 */ 1302 public abstract Map<String, UIComponent> getFacets(); 1303 1304 /** 1305 * <p>Return the number of facet {@link UIComponent}s that are 1306 * associated with this {@link UIComponent}. If there are no 1307 * facets, this method must return 0. The method must not cause 1308 * the creation of a facet component map.</p> 1309 * 1310 * <p>For backwards compatability with classes that extend UIComponent 1311 * directly, a default implementation is provided that simply calls 1312 * {@link #getFacets} and then calls the <code>size()</code> method on the 1313 * returned <code>Map</code>. A more optimized version of this method is 1314 * provided in {@link UIComponentBase#getFacetCount}. 1315 * 1316 * @since 1.2 1317 */ 1318 public int getFacetCount() { 1319 return (getFacets().size()); 1320 } 1321 1322 1323 1324 /** 1325 * <p>Convenience method to return the named facet, if it exists, or 1326 * <code>null</code> otherwise. If the requested facet does not 1327 * exist, the facets Map must not be created.</p> 1328 * 1329 * @param name Name of the desired facet 1330 */ 1331 public abstract UIComponent getFacet(String name); 1332 1333 1334 /** 1335 * <p>Return an <code>Iterator</code> over the facet followed by child 1336 * {@link UIComponent}s of this {@link UIComponent}. 1337 * Facets are returned in an undefined order, followed by 1338 * all the children in the order they are stored in the child list. If this 1339 * component has no facets or children, an empty <code>Iterator</code> 1340 * is returned.</p> 1341 * 1342 * <p>The returned <code>Iterator</code> must not support the 1343 * <code>remove()</code> operation.</p> 1344 */ 1345 public abstract Iterator<UIComponent> getFacetsAndChildren(); 1346 1347 1348 // -------------------------------------------- Lifecycle Processing Methods 1349 1350 1351 /** 1352 * <p>Broadcast the specified {@link FacesEvent} to all registered 1353 * event listeners who have expressed an interest in events of this 1354 * type. Listeners are called in the order in which they were 1355 * added.</p> 1356 * <p class="changed_added_2_0">If the <code>event</code> is an instance of 1357 * {@link javax.faces.event.BehaviorEvent} and the current 1358 * <code>component</code> is the source of the <code>event</code> 1359 * call {@link javax.faces.event.BehaviorEvent#getBehavior} to get the 1360 * {@link javax.faces.component.behavior.Behavior} for the event. If the 1361 * behavior implements {@link javax.faces.component.behavior.ClientBehavior}, 1362 * call {@link javax.faces.component.behavior.ClientBehavior#broadcast(javax.faces.event.BehaviorEvent)}}.</p> 1363 * 1364 * @param event The {@link FacesEvent} to be broadcast 1365 * 1366 * @throws AbortProcessingException Signal the JavaServer Faces 1367 * implementation that no further processing on the current event 1368 * should be performed 1369 * @throws IllegalArgumentException if the implementation class 1370 * of this {@link FacesEvent} is not supported by this component 1371 * @throws NullPointerException if <code>event</code> is 1372 * <code>null</code> 1373 */ 1374 public abstract void broadcast(FacesEvent event) 1375 throws AbortProcessingException; 1376 1377 1378 /** 1379 * <p>Decode any new state of this {@link UIComponent} from the 1380 * request contained in the specified {@link FacesContext}, and store 1381 * this state as needed.</p> 1382 * <p>During decoding, events may be enqueued for later processing 1383 * (by event listeners who have registered an interest), by calling 1384 * <code>queueEvent()</code>.</p> 1385 * 1386 * @param context {@link FacesContext} for the request we are processing 1387 * 1388 * @throws NullPointerException if <code>context</code> 1389 * is <code>null</code> 1390 */ 1391 public abstract void decode(FacesContext context); 1392 1393 /** 1394 * <p class="changed_added_2_0">Perform a tree visit starting at 1395 * this node in the tree.</p> 1396 * 1397 * <div class="changed_added_2_0"> 1398 * 1399 * <p>UIComponent.visitTree() implementations do not invoke the 1400 * {@link VisitCallback} directly, but instead call {@link 1401 * VisitContext#invokeVisitCallback} to invoke the callback. This 1402 * allows {@code VisitContext} implementations to provide optimized 1403 * tree traversals, for example by only calling the {@code 1404 * VisitCallback} for a subset of components.</p> 1405 * 1406 * <p>UIComponent.visitTree() implementations must call 1407 * UIComponent.pushComponentToEL() before performing the 1408 * visit and UIComponent.popComponentFromEL() after the 1409 * visit.</p> 1410 * 1411 * @param context the <code>VisitContext</code> for this visit 1412 * @param callback the <code>VisitCallback</code> instance 1413 * whose <code>visit</code> method will be called 1414 * for each node visited. 1415 * @return component implementations may return <code>true</code> 1416 * to indicate that the tree visit is complete (eg. all components 1417 * that need to be visited have been visited). This results in 1418 * the tree visit being short-circuited such that no more components 1419 * are visited. 1420 * 1421 * </div> 1422 * 1423 * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback() 1424 * 1425 * @since 2.0 1426 */ 1427 public boolean visitTree(VisitContext context, 1428 VisitCallback callback) { 1429 1430 // First check to see whether we are visitable. If not 1431 // short-circuit out of this subtree, though allow the 1432 // visit to proceed through to other subtrees. 1433 if (!isVisitable(context)) 1434 return false; 1435 1436 // Push ourselves to EL before visiting 1437 FacesContext facesContext = context.getFacesContext(); 1438 pushComponentToEL(facesContext, null); 1439 1440 try { 1441 // Visit ourselves. Note that we delegate to the 1442 // VisitContext to actually perform the visit. 1443 VisitResult result = context.invokeVisitCallback(this, callback); 1444 1445 // If the visit is complete, short-circuit out and end the visit 1446 if (result == VisitResult.COMPLETE) 1447 return true; 1448 1449 // Visit children if necessary 1450 if (result == VisitResult.ACCEPT) { 1451 Iterator<UIComponent> kids = this.getFacetsAndChildren(); 1452 1453 while(kids.hasNext()) { 1454 boolean done = kids.next().visitTree(context, callback); 1455 1456 // If any kid visit returns true, we are done. 1457 if (done) 1458 return true; 1459 } 1460 } 1461 } 1462 finally { 1463 // Pop ourselves off the EL stack 1464 popComponentFromEL(facesContext); 1465 } 1466 1467 // Return false to allow the visit to continue 1468 return false; 1469 } 1470 1471 /** 1472 * <p class="changed_added_2_0">Return <code>true</code> if this 1473 * component should be visited, <code>false</code> otherwise. 1474 * Called by {@link UIComponent#visitTree UIComponent.visitTree()} 1475 * to determine whether this component satisfies the hints returned 1476 * by {@link javax.faces.component.visit.VisitContext#getHints}.</p> 1477 1478 * <div class="changed_added_2_0"> 1479 1480 * <p>If this method returns false, the tree visited is 1481 * short-circuited such that neither the component nor any of its 1482 * descendents will be visited></p> 1483 1484 * <p>Custom {@code visitTree()} implementations may call this 1485 * method to determine whether the component is visitable before 1486 * performing any visit-related processing.</p> 1487 * 1488 * </div> 1489 * 1490 * @since 2.0 1491 */ 1492 protected boolean isVisitable(VisitContext context) { 1493 1494 // VisitHints currently defines two hints that affect 1495 // visitability: VIIST_RENDERED and VISIT_TRANSIENT. 1496 // Check for both of these and if set, verify that 1497 // we comply. 1498 Set<VisitHint> hints = context.getHints(); 1499 1500 if ((hints.contains(VisitHint.SKIP_UNRENDERED) && 1501 !this.isRendered()) || 1502 (hints.contains(VisitHint.SKIP_TRANSIENT) && 1503 this.isTransient())) { 1504 return false; 1505 } 1506 1507 return true; 1508 } 1509 1510 /** 1511 * <p><span class="changed_modified_2_0">If</span> our 1512 * <code>rendered</code> property is <code>true</code>, render the 1513 * beginning of the current state of this {@link UIComponent} to the 1514 * response contained in the specified {@link FacesContext}. 1515 * Call {@link #pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)}. 1516 * Call {@link javax.faces.application.Application#publishEvent}, passing 1517 * {@link javax.faces.event.PreRenderComponentEvent}<code>.class</code> as the 1518 * first argument and the component instance to be rendered as the 1519 * second argument.</p></li> 1520 1521 * <p>If a {@link Renderer} is associated with this {@link 1522 * UIComponent}, the actual encoding will be delegated to 1523 * {@link Renderer#encodeBegin(FacesContext, UIComponent)}. 1524 * </p> 1525 * 1526 * <p class="changed_added_2_0">If our <code>rendered</code> property is 1527 * <code>false</code>, call {@link #pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)} 1528 * and return immediately.</p> 1529 * 1530 * @param context {@link FacesContext} for the response we are creating 1531 * 1532 * @throws IOException if an input/output error occurs while rendering 1533 * @throws NullPointerException if <code>context</code> 1534 * is <code>null</code> 1535 */ 1536 public abstract void encodeBegin(FacesContext context) throws IOException; 1537 1538 1539 /** 1540 * <p>If our <code>rendered</code> property is <code>true</code>, 1541 * render the child {@link UIComponent}s of this {@link UIComponent}. 1542 * This method will only be called 1543 * if the <code>rendersChildren</code> property is <code>true</code>.</p> 1544 * 1545 * <p>If a {@link Renderer} is associated with this {@link UIComponent}, 1546 * the actual encoding will be delegated to 1547 * {@link Renderer#encodeChildren(FacesContext, UIComponent)}. 1548 * <span class="changed_modified_2_0">If no {@link Renderer} is associated 1549 * with this {@link UIComponent}, iterate over each of the children of this 1550 * component and call 1551 * {@link #encodeAll(javax.faces.context.FacesContext)}.</span></p> 1552 * 1553 * @param context {@link FacesContext} for the response we are creating 1554 * 1555 * @throws IOException if an input/output error occurs while rendering 1556 * @throws NullPointerException if <code>context</code> 1557 * is <code>null</code> 1558 */ 1559 public abstract void encodeChildren(FacesContext context) throws IOException; 1560 1561 1562 /** 1563 * <p><span class="changed_modified_2_0">If</span> our 1564 * <code>rendered</code> property is <code>true</code>, render the 1565 * ending of the current state of this {@link UIComponent}.</p> 1566 * 1567 * <p>If a {@link Renderer} is associated with this {@link UIComponent}, 1568 * the actual encoding will be delegated to 1569 * {@link Renderer#encodeEnd(FacesContext, UIComponent)}.</p> 1570 * 1571 * <p class="changed_added_2_0">Call {@link 1572 * UIComponent#popComponentFromEL}. before returning regardless of the value 1573 * of the <code>rendered</code> property.</p> 1574 * 1575 * @param context {@link FacesContext} for the response we are creating 1576 * 1577 * @throws IOException if an input/output error occurs while rendering 1578 * @throws NullPointerException if <code>context</code> 1579 * is <code>null</code> 1580 */ 1581 public abstract void encodeEnd(FacesContext context) throws IOException; 1582 1583 /** 1584 * <p>If this component 1585 * returns <code>true</code> from {@link #isRendered}, take the 1586 * following action.</p> 1587 * 1588 * <p>Render this component and all its children that return 1589 * <code>true</code> from <code>isRendered()</code>, regardless of 1590 * the value of the {@link #getRendersChildren} flag.</p></li> 1591 1592 * @since 1.2 1593 * 1594 * @throws IOException if an input/output error occurs while rendering 1595 * @throws NullPointerException if <code>context</code> 1596 * is <code>null</code> 1597 */ 1598 public void encodeAll(FacesContext context) throws IOException { 1599 1600 if (context == null) { 1601 throw new NullPointerException(); 1602 } 1603 1604 if (!isRendered()) { 1605 return; 1606 } 1607 1608 encodeBegin(context); 1609 if (getRendersChildren()) { 1610 encodeChildren(context); 1611 } else if (this.getChildCount() > 0) { 1612 for (UIComponent kid : getChildren()) { 1613 kid.encodeAll(context); 1614 } 1615 } 1616 1617 encodeEnd(context); 1618 1619 } 1620 1621 1622 private UIComponent previouslyPushed = null; 1623 private UIComponent previouslyPushedCompositeComponent = null; 1624 private boolean pushed; 1625 private int depth; 1626 1627 /** 1628 * <p class="changed_added_2_0">Push the current 1629 * <code>UIComponent</code> <code>this</code> to the {@link FacesContext} 1630 * attribute map using the key {@link #CURRENT_COMPONENT} saving the previous 1631 * <code>UIComponent</code> associated with {@link #CURRENT_COMPONENT} for a 1632 * subsequent call to {@link #popComponentFromEL}.</p> 1633 * 1634 * <pclass="changed_added_2_0">This method and <code>popComponentFromEL()</code> form the basis for 1635 * the contract that enables the EL Expression "<code>#{component}</code>" to 1636 * resolve to the "current" component that is being processed in the 1637 * lifecycle. The requirements for when <code>pushComponentToEL()</code> and 1638 * <code>popComponentFromEL()</code> must be called are specified as 1639 * needed in the javadoc for this class.</p> 1640 * 1641 * <p class="changed_added_2_0">After 1642 * <code>pushComponentToEL()</code> returns, a call to {@link 1643 * #getCurrentComponent} must return <code>this</code> 1644 * <code>UIComponent</code> instance until 1645 * <code>popComponentFromEL()</code> is called, after which point 1646 * the previous <code>UIComponent</code> instance will be returned 1647 * from <code>getCurrentComponent()</code></p> 1648 * 1649 * @param context the {@link FacesContext} for the current request 1650 * @param component the <code>component</code> to push to the EL. If 1651 * <code>component</code> is <code>null</code> the <code>UIComponent</code> 1652 * instance that this call was invoked upon will be pushed to the EL. 1653 * 1654 * @throws NullPointerException if <code>context</code> is <code>null</code> 1655 * 1656 * @see javax.faces.context.FacesContext#getAttributes() 1657 * 1658 * @since 2.0 1659 */ 1660 public final void pushComponentToEL(FacesContext context, UIComponent component) { 1661 1662 if (context == null) { 1663 throw new NullPointerException(); 1664 } 1665 1666 if (null == component) { 1667 component = this; 1668 } 1669 1670 Map<Object,Object> contextMap = context.getAttributes(); 1671 1672 if (contextMap.get(CURRENT_COMPONENT) == component) { 1673 depth++; 1674 return; 1675 } 1676 if (contextMap.get(CURRENT_COMPOSITE_COMPONENT) == component) { 1677 depth++; 1678 return; 1679 } 1680 1681 pushed = true; 1682 previouslyPushed = (UIComponent) contextMap.put(CURRENT_COMPONENT, component); 1683 // If this is a composite component... 1684 if (UIComponent.isCompositeComponent(component)) { 1685 // make it so #{cc} resolves to this composite 1686 // component, preserving the previous value if present 1687 previouslyPushedCompositeComponent = 1688 (UIComponent) contextMap.put(CURRENT_COMPOSITE_COMPONENT, component); 1689 } 1690 1691 } 1692 1693 1694 /** 1695 * <p class="changed_added_2_0">Pop the current 1696 * <code>UIComponent</code> from the {@link FacesContext} attributes map 1697 * so that the previous <code>UIComponent</code>, if any, becomes the current 1698 * component.</p> 1699 * 1700 * @param context the {@link FacesContext} for the current request 1701 * 1702 * @throws NullPointerException if <code>context</code> is <code>null</code> 1703 * 1704 * @see javax.faces.context.FacesContext#getAttributes() 1705 * 1706 * @since 2.0 1707 */ 1708 public final void popComponentFromEL(FacesContext context) { 1709 1710 if (context == null) { 1711 throw new NullPointerException(); 1712 } 1713 1714 if (depth > 0) { 1715 depth--; 1716 return; 1717 } 1718 1719 Map<Object,Object> contextMap = context.getAttributes(); 1720 if (contextMap != null) { 1721 1722 if (!pushed) { 1723 return; 1724 } 1725 UIComponent c; 1726 pushed = false; 1727 if (previouslyPushed != null) { 1728 c = (UIComponent) contextMap.put(CURRENT_COMPONENT, previouslyPushed); 1729 } else { 1730 c = (UIComponent) contextMap.remove(CURRENT_COMPONENT); 1731 } 1732 1733 if (c != null && UIComponent.isCompositeComponent(c)) { 1734 if (previouslyPushedCompositeComponent != null) { 1735 contextMap.put(CURRENT_COMPOSITE_COMPONENT, 1736 previouslyPushedCompositeComponent); 1737 } else { 1738 contextMap.remove(CURRENT_COMPOSITE_COMPONENT); 1739 } 1740 } 1741 } 1742 1743 } 1744 1745 1746 /** 1747 * <p class="changed_added_2_0">Return <code>true</code> if 1748 * <code>component</code> is a composite component, otherwise 1749 * <code>false</code>.</p> 1750 * 1751 * @param component the {@link UIComponent} to test 1752 * 1753 * @throws NullPointerException if <code>component</code> is <code>null</code> 1754 * @since 2.0 1755 */ 1756 public static boolean isCompositeComponent(UIComponent component) { 1757 1758 if (component == null) { 1759 throw new NullPointerException(); 1760 } 1761 return (component.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)); 1762 1763 } 1764 1765 1766 /** 1767 * <p> 1768 * Finds the nearest composite component parent of the specified component. 1769 * </p> 1770 * 1771 * @param component the component from which to start the search from 1772 * 1773 * @return if <code>component</code> is <code>null</code>, return 1774 * <code>null</code>, otherwise search the component's parent hierachy 1775 * for the nearest parent composite component. If no parent composite 1776 * component is found, return <code>null</code> 1777 * 1778 * @since 2.0 1779 */ 1780 public static UIComponent getCompositeComponentParent(UIComponent component) { 1781 1782 if (component == null) { 1783 return null; 1784 } else { 1785 if (component.compositeParent != null) { 1786 return component.compositeParent; 1787 } 1788 UIComponent parent = component.getParent(); 1789 while (parent != null) { 1790 if (UIComponent.isCompositeComponent(parent)) { 1791 if (component.isInView()) { 1792 component.compositeParent = parent; 1793 } 1794 return parent; 1795 } 1796 parent = parent.getParent(); 1797 } 1798 return null; 1799 } 1800 1801 } 1802 1803 1804 /** 1805 * <p class="changed_added_2_0">Return the <code>UIComponent</code> 1806 * instance that is currently processing. This is equivalent to 1807 * evaluating the EL expression "<code>#{component}</code>" and 1808 * doing a <code>getValue</code> operation on the resultant 1809 * <code>ValueExpression</code>.</p> 1810 * 1811 * <p class="changed_added_2_0">This method must return 1812 * <code>null</code> if there is no currently processing 1813 * <code>UIComponent</code></p> 1814 * 1815 * @param context {@link FacesContext} for the request we are processing 1816 * 1817 * @throws NullPointerException if <code>context</code> 1818 * is <code>null</code> 1819 * 1820 * @since 2.0 1821 */ 1822 public static UIComponent getCurrentComponent(FacesContext context) { 1823 if (context == null) { 1824 throw new NullPointerException(); 1825 } 1826 Map<Object, Object> contextMap = context.getAttributes(); 1827 return (UIComponent) contextMap.get(CURRENT_COMPONENT); 1828 1829 } 1830 1831 1832 /** 1833 * <p class="changed_added_2_0">Return the closest ancestor 1834 * component, relative to the component returned from {@link 1835 * #getCurrentComponent}, that is a composite component, or 1836 * <code>null</code> if no such component exists.</p> 1837 * 1838 * @param context {@link FacesContext} for the request we are processing 1839 * 1840 * @throws NullPointerException if <code>context</code> 1841 * is <code>null</code> 1842 * 1843 * @since 2.0 1844 */ 1845 public static UIComponent getCurrentCompositeComponent(FacesContext context) { 1846 if (context == null) { 1847 throw new NullPointerException(); 1848 } 1849 1850 Map<Object, Object> contextMap = context.getAttributes(); 1851 return (UIComponent) contextMap.get(CURRENT_COMPOSITE_COMPONENT); 1852 1853 } 1854 1855 // -------------------------------------------------- Event Listener Methods 1856 1857 1858 /** 1859 * <p>Add the specified {@link FacesListener} to the set of listeners 1860 * registered to receive event notifications from this {@link UIComponent}. 1861 * It is expected that {@link UIComponent} classes acting as event sources 1862 * will have corresponding typesafe APIs for registering listeners of the 1863 * required type, and the implementation of those registration methods 1864 * will delegate to this method. For example:</p> 1865 * <pre> 1866 * public class FooEvent extends FacesEvent { ... } 1867 * 1868 * public interface FooListener extends FacesListener { 1869 * public void processFoo(FooEvent event); 1870 * } 1871 * 1872 * public class FooComponent extends UIComponentBase { 1873 * ... 1874 * public void addFooListener(FooListener listener) { 1875 * addFacesListener(listener); 1876 * } 1877 * public void removeFooListener(FooListener listener) { 1878 * removeFacesListener(listener); 1879 * } 1880 * ... 1881 * } 1882 * </pre> 1883 * 1884 * @param listener The {@link FacesListener} to be registered 1885 * 1886 * @throws NullPointerException if <code>listener</code> 1887 * is <code>null</code> 1888 */ 1889 protected abstract void addFacesListener(FacesListener listener); 1890 1891 1892 /** 1893 * <p>Return an array of registered {@link FacesListener}s that are 1894 * instances of the specified class. If there are no such registered 1895 * listeners, a zero-length array is returned. The returned 1896 * array can be safely be cast to an array strongly typed to 1897 * an element type of <code>clazz</code>.</p> 1898 * 1899 * @param clazz Class that must be implemented by a {@link FacesListener} 1900 * for it to be returned 1901 * 1902 * @throws IllegalArgumentException if <code>class</code> is not, 1903 * and does not implement, {@link FacesListener} 1904 * @throws NullPointerException if <code>clazz</code> 1905 * is <code>null</code> 1906 */ 1907 protected abstract FacesListener[] getFacesListeners(Class clazz); 1908 1909 1910 /** 1911 * <p>Remove the specified {@link FacesListener} from the set of listeners 1912 * registered to receive event notifications from this {@link UIComponent}. 1913 * 1914 * @param listener The {@link FacesListener} to be deregistered 1915 * 1916 * @throws NullPointerException if <code>listener</code> 1917 * is <code>null</code> 1918 */ 1919 protected abstract void removeFacesListener(FacesListener listener); 1920 1921 1922 /** 1923 * <p>Queue an event for broadcast at the end of the current request 1924 * processing lifecycle phase. The default implementation in 1925 * {@link UIComponentBase} must delegate this call to the 1926 * <code>queueEvent()</code> method of the parent {@link UIComponent}.</p> 1927 * 1928 * @param event {@link FacesEvent} to be queued 1929 * 1930 * @throws IllegalStateException if this component is not a 1931 * descendant of a {@link UIViewRoot} 1932 * @throws NullPointerException if <code>event</code> 1933 * is <code>null</code> 1934 */ 1935 public abstract void queueEvent(FacesEvent event); 1936 1937 /** 1938 * <p class="changed_added_2_0">Install the listener instance 1939 * referenced by argument <code>componentListener</code> as a 1940 * listener for events of type <code>eventClass</code> originating 1941 * from this specific instance of <code>UIComponent</code>. The 1942 * default implementation creates an inner {@link 1943 * SystemEventListener} instance that wraps argument 1944 * <code>componentListener</code> as the <code>listener</code> 1945 * argument. This inner class must call through to the argument 1946 * <code>componentListener</code> in its implementation of {@link 1947 * SystemEventListener#processEvent} and its implementation of 1948 * {@link SystemEventListener#isListenerForSource} must return 1949 * true if the instance class of this <code>UIComponent</code> is 1950 * assignable from the argument to 1951 * <code>isListenerForSource</code>.</p> 1952 * 1953 * @param eventClass the <code>Class</code> of event for which 1954 * <code>listener</code> must be fired. 1955 * @param componentListener the implementation of {@link 1956 * ComponentSystemEventListener} whose {@link 1957 * ComponentSystemEventListener#processEvent} method must be called 1958 * when events of type <code>facesEventClass</code> are fired. 1959 * 1960 * @throws <code>NullPointerException</code> if any of the 1961 * arguments are <code>null</code>. 1962 * 1963 * @since 2.0 1964 */ 1965 public void subscribeToEvent(Class<? extends SystemEvent> eventClass, 1966 ComponentSystemEventListener componentListener) { 1967 1968 if (eventClass == null) { 1969 throw new NullPointerException(); 1970 } 1971 if (componentListener == null) { 1972 throw new NullPointerException(); 1973 } 1974 1975 if (initialStateMarked()) { 1976 initialState = false; 1977 } 1978 if (null == listenersByEventClass) { 1979 listenersByEventClass = new HashMap<Class<? extends SystemEvent>, 1980 List<SystemEventListener>>(3, 1.0f); 1981 } 1982 SystemEventListener facesLifecycleListener = 1983 new ComponentSystemEventListenerAdapter(componentListener, this); 1984 List<SystemEventListener> listenersForEventClass = 1985 listenersByEventClass.get(eventClass); 1986 if (listenersForEventClass == null) { 1987 listenersForEventClass = new ArrayList<SystemEventListener>(3); 1988 listenersByEventClass.put(eventClass, listenersForEventClass); 1989 } 1990 if (!listenersForEventClass.contains(facesLifecycleListener)) { 1991 listenersForEventClass.add(facesLifecycleListener); 1992 } 1993 1994 } 1995 1996 /** 1997 * <p class="changed_added_2_0">Remove the listener instance 1998 * referenced by argument <code>componentListener</code> as a 1999 * listener for events of type <code>eventClass</code> 2000 * originating from this specific instance of 2001 * <code>UIComponent</code>. When doing the comparison to 2002 * determine if an existing listener is equal to the argument 2003 * <code>componentListener</code> (and thus must be removed), 2004 * the <code>equals()</code> method on the <em>existing 2005 * listener</em> must be invoked, passing the argument 2006 * <code>componentListener</code>, rather than the other way 2007 * around.</p> 2008 * 2009 * @param eventClass the <code>Class</code> of event for which 2010 * <code>listener</code> must be removed. 2011 * @param componentListener the implementation of {@link 2012 * ComponentSystemEventListener} whose {@link 2013 * ComponentSystemEventListener#processEvent} method must no longer be called 2014 * when events of type <code>eventClass</code> are fired. 2015 * 2016 * @throws <code>NullPointerException</code> if any of the 2017 * arguments are <code>null</code>. 2018 * 2019 * @since 2.0 2020 */ 2021 public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass, 2022 ComponentSystemEventListener componentListener) { 2023 2024 if (eventClass == null) { 2025 throw new NullPointerException(); 2026 } 2027 if (componentListener == null) { 2028 throw new NullPointerException(); 2029 } 2030 2031 List<SystemEventListener> listeners = 2032 getListenersForEventClass(eventClass); 2033 if (listeners != null && !listeners.isEmpty()) { 2034 for (Iterator<SystemEventListener> i = listeners.iterator(); i.hasNext();) { 2035 SystemEventListener item = i.next(); 2036 ComponentSystemEventListenerAdapter csla = 2037 (ComponentSystemEventListenerAdapter) item; 2038 ComponentSystemEventListener l = csla.getWrapped(); 2039 if (l.equals(componentListener)) { 2040 i.remove(); 2041 break; 2042 } 2043 } 2044 } 2045 2046 } 2047 2048 Map<Class<? extends SystemEvent>, List<SystemEventListener>> listenersByEventClass; 2049 2050 /** 2051 * <p class="changed_added_2_0">Return the 2052 * <code>SystemEventListener</code> instances registered on this 2053 * <code>UIComponent</code> instance that are interested in events 2054 * of type <code>eventClass</code>.</p> 2055 * 2056 * @param eventClass the <code>Class</code> of event for which the 2057 * listeners must be returned. 2058 2059 * @throws NullPointerException if argument <code>eventClass</code> is <code>null</code>. 2060 * 2061 * @since 2.0 2062 */ 2063 public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass) { 2064 2065 if (eventClass == null) { 2066 throw new NullPointerException(); 2067 } 2068 List<SystemEventListener> result = null; 2069 if (listenersByEventClass != null) { 2070 result = listenersByEventClass.get(eventClass); 2071 } 2072 return result; 2073 2074 } 2075 2076 2077 /** 2078 * <p class="changed_added_2_0">Starting with "this", return the closest 2079 * component in the ancestry that is a <code>NamingContainer</code> 2080 * or <code>null</code> if none can be found.</p> 2081 * 2082 * @since 2.0 2083 */ 2084 public UIComponent getNamingContainer() { 2085 UIComponent namingContainer = this; 2086 while (namingContainer != null) { 2087 if (namingContainer instanceof NamingContainer) { 2088 return namingContainer; 2089 } 2090 namingContainer = namingContainer.getParent(); 2091 } 2092 return null; 2093 } 2094 2095 // ------------------------------------------------ Lifecycle Phase Handlers 2096 2097 2098 /** 2099 * <p><span class="changed_modified_2_0">Perform</span> the 2100 * component tree processing required by the <em>Restore View</em> 2101 * phase of the request processing lifecycle for all facets of this 2102 * component, all children of this component, and this component 2103 * itself, as follows.</p> <ul> <li 2104 * class="changed_modified_2_0">Call the <code>restoreState()</code> 2105 * method of this component.</li> 2106 * 2107 * <li class="changed_added_2_0">Call 2108 * {@link UIComponent#pushComponentToEL}. </li> 2109 2110 * <li>Call the <code>processRestoreState()</code> method of all 2111 * facets and children of this {@link UIComponent} in the order 2112 * determined by a call to <code>getFacetsAndChildren()</code>. 2113 * <span class="changed_added_2_0">After returning from the 2114 * <code>processRestoreState()</code> method on a child or facet, 2115 * call {@link UIComponent#popComponentFromEL}</span></li> 2116 2117 * </ul> 2118 * 2119 * <p>This method may not be called if the state saving method is 2120 * set to server.</p> 2121 * 2122 * @param context {@link FacesContext} for the request we are processing 2123 * 2124 * @throws NullPointerException if <code>context</code> 2125 * is <code>null</code> 2126 */ 2127 public abstract void processRestoreState(FacesContext context, 2128 Object state); 2129 2130 2131 /** 2132 * <p><span class="changed_modified_2_0">Perform</span> the 2133 * component tree processing required by the <em>Apply Request 2134 * Values</em> phase of the request processing lifecycle for all 2135 * facets of this component, all children of this component, and 2136 * this component itself, as follows.</p> 2137 2138 * <ul> 2139 * <li>If the <code>rendered</code> property of this {@link UIComponent} 2140 * is <code>false</code>, skip further processing.</li> 2141 * <li class="changed_added_2_0">Call {@link #pushComponentToEL}.</li> 2142 * <li>Call the <code>processDecodes()</code> method of all facets 2143 * and children of this {@link UIComponent}, in the order determined 2144 * by a call to <code>getFacetsAndChildren()</code>.</li> 2145 2146 * <li>Call the <code>decode()</code> method of this component.</li> 2147 2148 * <li>Call {@link #popComponentFromEL} from inside of a 2149 * <code>finally block, just before returning.</code></li> 2150 2151 2152 2153 * <li>If a <code>RuntimeException</code> is thrown during 2154 * decode processing, call {@link FacesContext#renderResponse} 2155 * and re-throw the exception.</li> 2156 * </ul> 2157 * 2158 * @param context {@link FacesContext} for the request we are processing 2159 * 2160 * @throws NullPointerException if <code>context</code> 2161 * is <code>null</code> 2162 */ 2163 public abstract void processDecodes(FacesContext context); 2164 2165 /** 2166 * <p class="changed_added_2_0">The default implementation performs 2167 * the following action. If the argument <code>event</code> is an 2168 * instance of {@link PostRestoreStateEvent}, call 2169 * <code>this.</code>{@link #getValueExpression} passing the literal 2170 * string “binding”, without the quotes, as the 2171 * argument. If the result is non-<code>null</code>, set the value 2172 * of the <code>ValueExpression</code> to be <code>this</code>.</p> 2173 */ 2174 2175 public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { 2176 if (event instanceof PostRestoreStateEvent) { 2177 assert(this == event.getComponent()); 2178 // if this component has a component value reference expression, 2179 // make sure to populate the ValueExpression for it. 2180 ValueExpression valueExpression; 2181 if (null != (valueExpression = this.getValueExpression("binding"))) { 2182 valueExpression.setValue(FacesContext.getCurrentInstance().getELContext(), 2183 this); 2184 } 2185 2186 } 2187 } 2188 2189 2190 2191 2192 /** 2193 * <p><span class="changed_modified_2_0">Perform</span> the 2194 * component tree processing required by the <em>Process 2195 * Validations</em> phase of the request processing lifecycle for 2196 * all facets of this component, all children of this component, and 2197 * this component itself, as follows.</p> 2198 2199 * <ul> 2200 * <li>If the <code>rendered</code> property of this {@link UIComponent} 2201 * is <code>false</code>, skip further processing.</li> 2202 * <li class="changed_added_2_0">Call {@link #pushComponentToEL}.</li> 2203 * <li>Call the <code>processValidators()</code> method of all facets 2204 * and children of this {@link UIComponent}, in the order determined 2205 * by a call to <code>getFacetsAndChildren()</code>.</li> 2206 * </ul> 2207 * 2208 * @param context {@link FacesContext} for the request we are processing 2209 * 2210 * @throws NullPointerException if <code>context</code> 2211 * is <code>null</code> 2212 */ 2213 public abstract void processValidators(FacesContext context); 2214 2215 2216 /** 2217 * <p><span class="changed_modified_2_0">Perform</span> the 2218 * component tree processing required by the <em>Update Model 2219 * Values</em> phase of the request processing lifecycle for all 2220 * facets of this component, all children of this component, and 2221 * this component itself, as follows.</p> 2222 2223 * <ul> 2224 2225 * <li>If the <code>rendered</code> property of this {@link 2226 * UIComponent} is <code>false</code>, skip further processing.</li> 2227 2228 * <li class="changed_added_2_0">Call {@link 2229 * #pushComponentToEL}.</li> 2230 2231 * <li>Call the <code>processUpdates()</code> method of all facets 2232 * and children of this {@link UIComponent}, in the order determined 2233 * by a call to <code>getFacetsAndChildren()</code>. <span 2234 * class="changed_added_2_0">After returning from the 2235 * <code>processUpdates()</code> method on a child or facet, call 2236 * {@link UIComponent#popComponentFromEL}</span></li> 2237 2238 * </ul> 2239 * 2240 * @param context {@link FacesContext} for the request we are processing 2241 * 2242 * @throws NullPointerException if <code>context</code> 2243 * is <code>null</code> 2244 */ 2245 public abstract void processUpdates(FacesContext context); 2246 2247 2248 /** 2249 * <p><span class="changed_modified_2_0">Perform</span> the 2250 * component tree processing required by the state saving portion of 2251 * the <em>Render Response</em> phase of the request processing 2252 * lifecycle for all facets of this component, all children of this 2253 * component, and this component itself, as follows.</p> 2254 2255 * <ul> 2256 * 2257 * <li>consult the <code>transient</code> property of this 2258 * component. If true, just return <code>null</code>.</li> 2259 2260 * <li class="changed_added_2_0">Call {@link 2261 * #pushComponentToEL}.</li> 2262 2263 * <li>Call the <code>processSaveState()</code> method of all facets 2264 * and children of this {@link UIComponent} in the order determined 2265 * by a call to <code>getFacetsAndChildren()</code>, skipping 2266 * children and facets that are transient. Ensure that {@link 2267 * #popComponentFromEL} is called correctly after each child or 2268 * facet.</li> 2269 * 2270 * <li>Call the <code>saveState()</code> method of this component.</li> 2271 * 2272 * <li>Encapsulate the child state and your state into a 2273 * Serializable Object and return it.</li> 2274 * 2275 * </ul> 2276 * 2277 * <p>This method may not be called if the state saving method is 2278 * set to server.</p> 2279 * 2280 * @param context {@link FacesContext} for the request we are processing 2281 * 2282 * @throws NullPointerException if <code>context</code> 2283 * is <code>null</code> 2284 */ 2285 public abstract Object processSaveState(FacesContext context); 2286 2287 2288 // ----------------------------------------------------- Convenience Methods 2289 2290 2291 /** 2292 * <p>Convenience method to return the {@link FacesContext} instance 2293 * for the current request.</p> 2294 */ 2295 protected abstract FacesContext getFacesContext(); 2296 2297 2298 /** 2299 * <p>Convenience method to return the {@link Renderer} instance 2300 * associated with this component, if any; otherwise, return 2301 * <code>null</code>.</p> 2302 * 2303 * @param context {@link FacesContext} for the current request 2304 */ 2305 protected abstract Renderer getRenderer(FacesContext context); 2306 2307 2308 // --------------------------------------------------------- Package Private 2309 2310 2311 static final class ComponentSystemEventListenerAdapter 2312 implements SystemEventListener, StateHolder, FacesWrapper<ComponentSystemEventListener> { 2313 2314 ComponentSystemEventListener wrapped; 2315 Class<?> instanceClass; 2316 2317 2318 // -------------------------------------------------------- Constructors 2319 2320 2321 ComponentSystemEventListenerAdapter() { 2322 2323 // necessary for state saving 2324 2325 } 2326 2327 2328 ComponentSystemEventListenerAdapter(ComponentSystemEventListener wrapped, 2329 UIComponent component) { 2330 2331 this.wrapped = wrapped; 2332 this.instanceClass = component.getClass(); 2333 2334 } 2335 2336 2337 // ------------------------------------ Methods from SystemEventListener 2338 2339 2340 public void processEvent(SystemEvent event) throws AbortProcessingException { 2341 2342 wrapped.processEvent((ComponentSystemEvent) event); 2343 2344 } 2345 2346 2347 public boolean isListenerForSource(Object component) { 2348 2349 if (wrapped instanceof SystemEventListener) { 2350 return ((SystemEventListener) wrapped).isListenerForSource(component); 2351 } else { 2352 return instanceClass.isAssignableFrom(component.getClass()); 2353 } 2354 2355 } 2356 2357 2358 // -------------------------------------------- Methods from StateHolder 2359 2360 public Object saveState(FacesContext context) { 2361 2362 if (context == null) { 2363 throw new NullPointerException(); 2364 } 2365 return new Object[] { 2366 ((wrapped instanceof UIComponent) ? null : new StateHolderSaver(context, wrapped)), 2367 instanceClass 2368 }; 2369 2370 } 2371 2372 2373 public void restoreState(FacesContext context, Object state) { 2374 2375 if (context == null) { 2376 throw new NullPointerException(); 2377 } 2378 if (state == null) { 2379 return; 2380 } 2381 Object[] s = (Object[]) state; 2382 Object listener = s[0]; 2383 wrapped = (ComponentSystemEventListener) ((listener == null) 2384 ? UIComponent .getCurrentComponent(context) 2385 : ((StateHolderSaver) listener).restore(context)); 2386 instanceClass = (Class<?>) s[1]; 2387 2388 } 2389 2390 2391 public boolean isTransient() { 2392 2393 if (wrapped instanceof StateHolder) { 2394 return ((StateHolder) wrapped).isTransient(); 2395 } 2396 return false; 2397 2398 } 2399 2400 2401 public void setTransient(boolean newTransientValue) { 2402 2403 // no-op 2404 2405 } 2406 2407 2408 // ------------------------------------------- Methods from FacesWrapper 2409 2410 2411 public ComponentSystemEventListener getWrapped() { 2412 2413 return wrapped; 2414 2415 } 2416 2417 2418 // ------------------------------------------------------ Public Methods 2419 2420 2421 @Override 2422 public int hashCode() { 2423 2424 return (wrapped.hashCode() ^ instanceClass.hashCode()); 2425 2426 } 2427 2428 @Override 2429 public boolean equals(Object obj) { 2430 2431 if (!(obj instanceof ComponentSystemEventListenerAdapter)) { 2432 return false; 2433 } 2434 ComponentSystemEventListenerAdapter in = 2435 (ComponentSystemEventListenerAdapter) obj; 2436 return (wrapped.equals(in.wrapped) 2437 && instanceClass.equals(in.instanceClass)); 2438 2439 } 2440 } // END ComponentSystemEventListenerAdapter 2441 2442 }