1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. 5 * 6 * The contents of this file are subject to the terms of either the GNU 7 * General Public License Version 2 only ("GPL") or the Common Development 8 * and Distribution License("CDDL") (collectively, the "License"). You 9 * may not use this file except in compliance with the License. You can obtain 10 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html 11 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific 12 * language governing permissions and limitations under the License. 13 * 14 * When distributing the software, include this License Header Notice in each 15 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. 16 * Sun designates this particular file as subject to the "Classpath" exception 17 * as provided by Sun in the GPL Version 2 section of the License file that 18 * accompanied this code. If applicable, add the following below the License 19 * Header, with the fields enclosed by brackets [] replaced by your own 20 * identifying information: "Portions Copyrighted [year] 21 * [name of copyright owner]" 22 * 23 * Contributor(s): 24 * 25 * If you wish your version of this file to be governed by only the CDDL or 26 * only the GPL Version 2, indicate your decision by adding "[Contributor] 27 * elects to include this software in this distribution under the [CDDL or GPL 28 * Version 2] license." If you don't indicate a single choice of license, a 29 * recipient has the option to distribute your version of this file under 30 * either the CDDL, the GPL Version 2 or to extend the choice of license to 31 * its licensees as provided above. However, if you add GPL Version 2 code 32 * and therefore, elected the GPL Version 2 license, then the option applies 33 * only if the new code is made subject to such option by the copyright 34 * holder. 35 */ 36 37 package javax.faces.component; 38 39 import javax.el.ELException; 40 import javax.el.ValueExpression; 41 import javax.faces.FacesException; 42 import javax.faces.application.Application; 43 import javax.faces.component.behavior.Behavior; 44 import javax.faces.component.behavior.ClientBehavior; 45 import javax.faces.component.behavior.ClientBehaviorHolder; 46 import javax.faces.context.FacesContext; 47 import javax.faces.el.ValueBinding; 48 import javax.faces.event.AbortProcessingException; 49 import javax.faces.event.BehaviorEvent; 50 import javax.faces.event.FacesEvent; 51 import javax.faces.event.FacesListener; 52 import javax.faces.event.PostAddToViewEvent; 53 import javax.faces.event.PostValidateEvent; 54 import javax.faces.event.PreRemoveFromViewEvent; 55 import javax.faces.event.PreRenderComponentEvent; 56 import javax.faces.event.PreValidateEvent; 57 import javax.faces.event.SystemEvent; 58 import javax.faces.event.SystemEventListener; 59 import javax.faces.render.Renderer; 60 import java.beans.IntrospectionException; 61 import java.beans.Introspector; 62 import java.beans.PropertyDescriptor; 63 import java.io.IOException; 64 import java.io.ObjectInputStream; 65 import java.io.ObjectOutputStream; 66 import java.io.Serializable; 67 import java.lang.reflect.InvocationTargetException; 68 import java.lang.reflect.Method; 69 import java.lang.reflect.Array; 70 import java.util.AbstractCollection; 71 import java.util.AbstractMap; 72 import java.util.AbstractSet; 73 import java.util.ArrayList; 74 import java.util.Collection; 75 import java.util.Collections; 76 import java.util.HashMap; 77 import java.util.Iterator; 78 import java.util.List; 79 import java.util.ListIterator; 80 import java.util.Map; 81 import java.util.NoSuchElementException; 82 import java.util.Set; 83 import java.util.WeakHashMap; 84 import java.util.Map.Entry; 85 import java.util.logging.Level; 86 import java.util.logging.Logger; 87 88 /** 89 * <p><strong class="changed_modified_2_0">UIComponentBase</strong> is a 90 * convenience base class that implements the default concrete behavior 91 * of all methods defined by {@link UIComponent}.</p> 92 * <p/> 93 * <p>By default, this class defines <code>getRendersChildren()</code> 94 * to find the renderer for this component and call its 95 * <code>getRendersChildren()</code> method. The default implementation 96 * on the <code>Renderer</code> returns <code>false</code>. As of 97 * version 1.2 of the JavaServer Faces Specification, component authors 98 * are encouraged to return <code>true</code> from this method and rely 99 * on the implementation of {@link #encodeChildren} in this class and in 100 * the Renderer ({@link Renderer#encodeChildren}). Subclasses that wish 101 * to manage the rendering of their children should override this method 102 * to return <code>true</code> instead.</p> 103 */ 104 public abstract class UIComponentBase extends UIComponent { 105 106 107 // -------------------------------------------------------------- Attributes 108 109 private static Logger LOGGER = Logger.getLogger("javax.faces.component", 110 "javax.faces.LogStrings"); 111 112 private static final String ADDED = UIComponentBase.class.getName() + ".ADDED"; 113 114 115 /** 116 * <p>Each entry is an map of <code>PropertyDescriptor</code>s describing 117 * the properties of a concrete {@link UIComponent} implementation, keyed 118 * by the corresponding <code>java.lang.Class</code>.</p> 119 * <p/> 120 * <p><strong>IMPLEMENTATION NOTE</strong> - This is implemented as a 121 * <code>WeakHashMap</code> so that, even if this class is embedded in a 122 * container's class loader that is a parent to webapp class loaders, 123 * references to the classes will eventually expire.</p> 124 */ 125 @SuppressWarnings({"CollectionWithoutInitialCapacity"}) 126 private static Map<Class<?>, Map<String, PropertyDescriptor>> 127 descriptors = 128 new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>(); 129 130 /** 131 * Reference to the map of <code>PropertyDescriptor</code>s for this class 132 * in the <code>descriptors<code> <code>Map<code>. 133 */ 134 private Map<String, PropertyDescriptor> pdMap = null; 135 136 /** 137 * <p>An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods.</p> 138 */ 139 private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0]; 140 141 public UIComponentBase() { 142 populateDescriptorsMapIfNecessary(); 143 } 144 145 private void populateDescriptorsMapIfNecessary() { 146 Class<?> clazz = this.getClass(); 147 pdMap = descriptors.get(clazz); 148 if (null != pdMap) { 149 return; 150 } 151 152 // load the property descriptors for this class. 153 PropertyDescriptor pd[] = getPropertyDescriptors(); 154 if (pd != null) { 155 pdMap = new HashMap<String, PropertyDescriptor>(pd.length, 1.0f); 156 for (PropertyDescriptor aPd : pd) { 157 pdMap.put(aPd.getName(), aPd); 158 } 159 if (LOGGER.isLoggable(Level.FINE)) { 160 LOGGER.log(Level.FINE, "fine.component.populating_descriptor_map", 161 new Object[]{clazz, 162 Thread.currentThread().getName()}); 163 } 164 165 // Check again 166 Map<String, PropertyDescriptor> reCheckMap = 167 descriptors.get(clazz); 168 if (null != reCheckMap) { 169 return; 170 } 171 descriptors.put(clazz, pdMap); 172 } 173 174 175 } 176 177 178 /** 179 * <p>Return an array of <code>PropertyDescriptors</code> for this 180 * {@link UIComponent}'s implementation class. If no descriptors 181 * can be identified, a zero-length array will be returned.</p> 182 * 183 * @throws FacesException if an introspection exception occurs 184 */ 185 private PropertyDescriptor[] getPropertyDescriptors() { 186 PropertyDescriptor[] pd; 187 try { 188 pd = Introspector.getBeanInfo(this.getClass()). 189 getPropertyDescriptors(); 190 } catch (IntrospectionException e) { 191 throw new FacesException(e); 192 } 193 return (pd); 194 } 195 196 197 /** 198 * <p>The <code>Map</code> containing our attributes, keyed by 199 * attribute name.</p> 200 */ 201 private AttributesMap attributes = null; 202 203 204 public Map<String, Object> getAttributes() { 205 206 if (attributes == null) { 207 attributes = new AttributesMap(this); 208 } 209 return (attributes); 210 211 } 212 213 214 // ---------------------------------------------------------------- Bindings 215 216 217 /** 218 * {@inheritDoc} 219 * 220 * @throws NullPointerException {@inheritDoc} 221 * @deprecated This has been replaced by {@link #getValueExpression}. 222 */ 223 public ValueBinding getValueBinding(String name) { 224 225 if (name == null) { 226 throw new NullPointerException(); 227 } 228 ValueBinding result = null; 229 ValueExpression ve; 230 231 if (null != (ve = getValueExpression(name))) { 232 // if the ValueExpression is an instance of our private 233 // wrapper class. 234 if (ve.getClass().equals(ValueExpressionValueBindingAdapter.class)) { 235 result = ((ValueExpressionValueBindingAdapter) ve).getWrapped(); 236 } else { 237 // otherwise, this is a real ValueExpression. Wrap it 238 // in a ValueBinding. 239 result = new ValueBindingValueExpressionAdapter(ve); 240 } 241 } 242 return result; 243 } 244 245 246 /** 247 * {@inheritDoc} 248 * 249 * @throws IllegalArgumentException {@inheritDoc} 250 * @throws NullPointerException {@inheritDoc} 251 * @deprecated This has been replaced by {@link #setValueExpression}. 252 */ 253 public void setValueBinding(String name, ValueBinding binding) { 254 if (name == null) { 255 throw new NullPointerException(); 256 } 257 if (binding != null) { 258 ValueExpressionValueBindingAdapter adapter = 259 new ValueExpressionValueBindingAdapter(binding); 260 setValueExpression(name, adapter); 261 } else { 262 setValueExpression(name, null); 263 } 264 265 } 266 267 268 // -------------------------------------------------------------- Properties 269 270 271 /** 272 * <p>The assigned client identifier for this component.</p> 273 */ 274 private String clientId = null; 275 276 277 /** 278 * @throws NullPointerException {@inheritDoc} 279 */ 280 public String getClientId(FacesContext context) { 281 282 if (context == null) { 283 throw new NullPointerException(); 284 } 285 286 // if the clientId is not yet set 287 if (this.clientId == null) { 288 UIComponent namingContainerAncestor = 289 this.getNamingContainerAncestor(); 290 UIComponent parent = namingContainerAncestor; 291 String parentId = null; 292 293 // give the parent the opportunity to first 294 // grab a unique clientId 295 if (parent != null) { 296 parentId = parent.getContainerClientId(context); 297 } 298 299 // now resolve our own client id 300 this.clientId = getId(); 301 if (this.clientId == null) { 302 String generatedId; 303 if (null != namingContainerAncestor && 304 namingContainerAncestor instanceof UniqueIdVendor) { 305 generatedId = ((UniqueIdVendor)namingContainerAncestor).createUniqueId(context, null); 306 } 307 else { 308 generatedId = context.getViewRoot().createUniqueId(); 309 } 310 setId(generatedId); 311 this.clientId = getId(); 312 } 313 if (parentId != null) { 314 StringBuilder idBuilder = 315 new StringBuilder(parentId.length() 316 + 1 317 + this.clientId.length()); 318 this.clientId = idBuilder.append(parentId) 319 .append(UINamingContainer.getSeparatorChar(context)) 320 .append(this.clientId).toString(); 321 } 322 323 // allow the renderer to convert the clientId 324 Renderer renderer = this.getRenderer(context); 325 if (renderer != null) { 326 this.clientId = renderer.convertClientId(context, this.clientId); 327 } 328 } 329 return this.clientId; 330 } 331 332 /** 333 * <p>The component identifier for this component.</p> 334 */ 335 private String id = null; 336 337 338 public String getId() { 339 340 return (id); 341 342 } 343 344 private UIComponent getNamingContainerAncestor() { 345 UIComponent namingContainer = this.getParent(); 346 while (namingContainer != null) { 347 if (namingContainer instanceof NamingContainer) { 348 return namingContainer; 349 } 350 namingContainer = namingContainer.getParent(); 351 } 352 return null; 353 } 354 355 356 /** 357 * @throws IllegalArgumentException {@inheritDoc} 358 * @throws IllegalStateException {@inheritDoc} 359 */ 360 public void setId(String id) { 361 362 // if the current ID is not null, and the passed 363 // argument is the same, no need to validate it 364 // as it has already been validated. 365 if (this.id == null || !(this.id.equals(id))) { 366 validateId(id); 367 this.id = id; 368 } 369 370 this.clientId = null; // Erase any cached value 371 372 } 373 374 375 /** 376 * <p>The parent component for this component.</p> 377 */ 378 private UIComponent parent = null; 379 380 381 public UIComponent getParent() { 382 return (this.parent); 383 } 384 385 386 public void setParent(UIComponent parent) { 387 388 389 if (parent == null) { 390 if (this.parent != null) { 391 doPreRemoveProcessing(FacesContext.getCurrentInstance(), this); 392 this.parent = parent; 393 } 394 compositeParent = null; 395 } else { 396 this.parent = parent; 397 if (this.getAttributes().get(ADDED) == null) { 398 // add an attribute to this component here to indiciate that 399 // it's being processed. If we don't do this, and the component 400 // is re-parented, the events could fire again in certain cases 401 // and cause a stack overflow. 402 this.getAttributes().put(ADDED, Boolean.TRUE); 403 doPostAddProcessing(FacesContext.getCurrentInstance(), this); 404 // remove the attribute once we've returned from the event 405 // processing. 406 this.getAttributes().remove(ADDED); 407 } 408 } 409 410 } 411 412 413 414 public boolean isRendered() { 415 416 return Boolean.valueOf(getStateHelper().eval(PropertyKeys.rendered, Boolean.TRUE).toString()); 417 } 418 419 420 public void setRendered(boolean rendered) { 421 getStateHelper().put(PropertyKeys.rendered, rendered); 422 } 423 424 425 public String getRendererType() { 426 427 return (String) getStateHelper().eval(PropertyKeys.rendererType); 428 429 } 430 431 432 public void setRendererType(String rendererType) { 433 434 getStateHelper().put(PropertyKeys.rendererType, rendererType); 435 436 } 437 438 439 public boolean getRendersChildren() { 440 boolean result = false; 441 442 Renderer renderer; 443 if (getRendererType() != null) { 444 if (null != 445 (renderer = getRenderer(getFacesContext()))) { 446 result = renderer.getRendersChildren(); 447 } 448 } 449 return result; 450 451 } 452 453 // ------------------------------------------------- Tree Management Methods 454 455 456 /* 457 * <p>The <code>List</code> containing our child components.</p> 458 */ 459 private List<UIComponent> children = null; 460 461 462 public List<UIComponent> getChildren() { 463 464 if (children == null) { 465 children = new ChildrenList(this); 466 } 467 return (children); 468 469 } 470 471 472 // Do not allocate the children List to answer this question 473 public int getChildCount() { 474 475 if (children != null) { 476 return (children.size()); 477 } else { 478 return (0); 479 } 480 481 } 482 483 484 /** 485 * <p>If the specified {@link UIComponent} has a non-null parent, 486 * remove it as a child or facet (as appropriate) of that parent. 487 * As a result, the <code>parent</code> property will always be 488 * <code>null</code> when this method returns.</p> 489 * 490 * @param component {@link UIComponent} to have any parent erased 491 */ 492 private static void eraseParent(UIComponent component) { 493 494 UIComponent parent = component.getParent(); 495 if (parent == null) { 496 return; 497 } 498 if (parent.getChildCount() > 0) { 499 List children = parent.getChildren(); 500 int index = children.indexOf(component); 501 if (index >= 0) { 502 children.remove(index); 503 return; 504 } 505 } 506 if (parent.getFacetCount() > 0) { 507 Map facets = parent.getFacets(); 508 Iterator entries = facets.entrySet().iterator(); 509 while (entries.hasNext()) { 510 Map.Entry entry = (Map.Entry) entries.next(); 511 //noinspection ObjectEquality 512 if (entry.getValue() == component) { 513 entries.remove(); 514 return; 515 } 516 } 517 } 518 519 // Throw an exception for the "cannot happen" case 520 throw new IllegalStateException("Parent was not null, " + 521 "but this component not related"); 522 523 } 524 525 /** 526 * <p>Throw <code>IllegalArgumentException</code> if the specified 527 * component identifier is non-<code>null</code> and not 528 * syntactically valid. </p> 529 * 530 * @param id The component identifier to test 531 */ 532 private static void validateId(String id) { 533 534 if (id == null) { 535 return; 536 } 537 int n = id.length(); 538 if (n < 1) { 539 throw new IllegalArgumentException("Empty id attribute is not allowed"); 540 } 541 for (int i = 0; i < n; i++) { 542 char c = id.charAt(i); 543 if (i == 0) { 544 if (!Character.isLetter(c) && (c != '_')) { 545 throw new IllegalArgumentException(id); 546 } 547 } else { 548 if (!Character.isLetter(c) && 549 !Character.isDigit(c) && 550 (c != '-') && (c != '_')) { 551 throw new IllegalArgumentException(id); 552 } 553 } 554 } 555 556 } 557 558 559 /** 560 * @throws NullPointerException {@inheritDoc} 561 */ 562 public UIComponent findComponent(String expr) { 563 if (expr == null) { 564 throw new NullPointerException(); 565 } 566 567 FacesContext ctx = FacesContext.getCurrentInstance(); 568 final char sepChar = UINamingContainer.getSeparatorChar(ctx); 569 final String SEPARATOR_STRING = String.valueOf(sepChar); 570 571 if (expr.length() == 0) { 572 // if an empty value is provided, fail fast. 573 throw new IllegalArgumentException("\"\""); 574 } 575 576 // Identify the base component from which we will perform our search 577 UIComponent base = this; 578 if (expr.charAt(0) == sepChar) { 579 // Absolute searches start at the root of the tree 580 while (base.getParent() != null) { 581 base = base.getParent(); 582 } 583 // Treat remainder of the expression as relative 584 expr = expr.substring(1); 585 } else if (!(base instanceof NamingContainer)) { 586 // Relative expressions start at the closest NamingContainer or root 587 while (base.getParent() != null) { 588 if (base instanceof NamingContainer) { 589 break; 590 } 591 base = base.getParent(); 592 } 593 } 594 595 // Evaluate the search expression (now guaranteed to be relative) 596 UIComponent result = null; 597 String[] segments = expr.split(SEPARATOR_STRING); 598 for (int i = 0, length = (segments.length - 1); 599 i < segments.length; 600 i++, length--) { 601 result = findComponent(base, segments[i], (i == 0)); 602 // the first element of the expression may match base.id 603 // (vs. a child if of base) 604 if (i == 0 && result == null && 605 segments[i].equals(base.getId())) { 606 result = base; 607 } 608 if (result != null && (!(result instanceof NamingContainer)) && length > 0) { 609 throw new IllegalArgumentException(segments[i]); 610 } 611 if (result == null) { 612 break; 613 } 614 base = result; 615 } 616 617 // Return the final result of our search 618 return (result); 619 620 } 621 622 623 /** 624 * <p>Return the {@link UIComponent} (if any) with the specified 625 * <code>id</code>, searching recursively starting at the specified 626 * <code>base</code>, and examining the base component itself, followed 627 * by examining all the base component's facets and children (unless 628 * the base component is a {@link NamingContainer}, in which case the 629 * recursive scan is skipped.</p> 630 * 631 * @param base Base {@link UIComponent} from which to search 632 * @param id Component identifier to be matched 633 */ 634 private static UIComponent findComponent(UIComponent base, 635 String id, 636 boolean checkId) { 637 if (checkId && id.equals(base.getId())) { 638 return base; 639 } 640 // Search through our facets and children 641 UIComponent result = null; 642 for (Iterator i = base.getFacetsAndChildren(); i.hasNext();) { 643 UIComponent kid = (UIComponent) i.next(); 644 if (!(kid instanceof NamingContainer)) { 645 if (checkId && id.equals(kid.getId())) { 646 result = kid; 647 break; 648 } 649 result = findComponent(kid, id, true); 650 if (result != null) { 651 break; 652 } 653 } else if (id.equals(kid.getId())) { 654 result = kid; 655 break; 656 } 657 } 658 return (result); 659 660 } 661 662 /** 663 * {@inheritDoc} 664 * 665 * @throws NullPointerException {@inheritDoc} 666 * @throws FacesException {@inheritDoc} 667 * @since 1.2 668 */ 669 public boolean invokeOnComponent(FacesContext context, String clientId, 670 ContextCallback callback) 671 throws FacesException { 672 return super.invokeOnComponent(context, clientId, callback); 673 } 674 675 676 // ------------------------------------------------ Facet Management Methods 677 678 679 /* 680 * <p>The <code>Map</code> containing our related facet components.</p> 681 */ 682 private Map<String, UIComponent> facets = null; 683 684 685 public Map<String, UIComponent> getFacets() { 686 687 if (facets == null) { 688 facets = new FacetsMap(this); 689 } 690 return (facets); 691 692 } 693 694 // Do not allocate the children List to answer this question 695 public int getFacetCount() { 696 697 if (facets != null) { 698 return (facets.size()); 699 } else { 700 return (0); 701 } 702 703 } 704 705 706 // Do not allocate the facets Map to answer this question 707 public UIComponent getFacet(String name) { 708 709 if (facets != null) { 710 return (facets.get(name)); 711 } else { 712 return (null); 713 } 714 715 } 716 717 718 public Iterator<UIComponent> getFacetsAndChildren() { 719 720 Iterator<UIComponent> result; 721 int childCount = this.getChildCount(), 722 facetCount = this.getFacetCount(); 723 // If there are neither facets nor children 724 if (0 == childCount && 0 == facetCount) { 725 result = EMPTY_ITERATOR; 726 } 727 // If there are only facets and no children 728 else if (0 == childCount) { 729 Collection<UIComponent> unmodifiable = 730 Collections.unmodifiableCollection(getFacets().values()); 731 result = unmodifiable.iterator(); 732 } 733 // If there are only children and no facets 734 else if (0 == facetCount) { 735 List<UIComponent> unmodifiable = 736 Collections.unmodifiableList(getChildren()); 737 result = unmodifiable.iterator(); 738 } 739 // If there are both children and facets 740 else { 741 result = new FacetsAndChildrenIterator(this); 742 } 743 return result; 744 } 745 746 747 // -------------------------------------------- Lifecycle Processing Methods 748 749 /** 750 * @throws AbortProcessingException {@inheritDoc} 751 * @throws IllegalStateException {@inheritDoc} 752 * @throws NullPointerException {@inheritDoc} 753 */ 754 public void broadcast(FacesEvent event) 755 throws AbortProcessingException { 756 757 if (event == null) { 758 throw new NullPointerException(); 759 } 760 if (event instanceof BehaviorEvent) { 761 BehaviorEvent behaviorEvent = (BehaviorEvent) event; 762 Behavior behavior = behaviorEvent.getBehavior(); 763 behavior.broadcast(behaviorEvent); 764 } 765 766 if (listeners == null) { 767 return; 768 } 769 770 for (FacesListener listener : listeners.asArray(FacesListener.class)) { 771 if (event.isAppropriateListener(listener)) { 772 event.processListener(listener); 773 } 774 } 775 } 776 777 778 /** 779 * @throws NullPointerException {@inheritDoc} 780 */ 781 public void decode(FacesContext context) { 782 783 if (context == null) { 784 throw new NullPointerException(); 785 } 786 String rendererType = getRendererType(); 787 if (rendererType != null) { 788 Renderer renderer = this.getRenderer(context); 789 if (renderer != null) { 790 renderer.decode(context, this); 791 } else { 792 if (LOGGER.isLoggable(Level.FINE)) { 793 LOGGER.fine("Can't get Renderer for type " + rendererType); 794 } 795 } 796 } 797 } 798 799 800 /** 801 * @throws NullPointerException {@inheritDoc} 802 */ 803 public void encodeBegin(FacesContext context) throws IOException { 804 805 if (context == null) { 806 throw new NullPointerException(); 807 } 808 809 pushComponentToEL(context, null); 810 811 if (!isRendered()) { 812 return; 813 } 814 815 context.getApplication().publishEvent(context, 816 PreRenderComponentEvent.class, 817 this); 818 819 String rendererType = getRendererType(); 820 if (rendererType != null) { 821 Renderer renderer = this.getRenderer(context); 822 if (renderer != null) { 823 renderer.encodeBegin(context, this); 824 } else { 825 if (LOGGER.isLoggable(Level.FINE)) { 826 LOGGER.fine("Can't get Renderer for type " + rendererType); 827 } 828 } 829 } 830 831 } 832 833 /** 834 * @throws NullPointerException {@inheritDoc} 835 */ 836 public void encodeChildren(FacesContext context) throws IOException { 837 838 if (context == null) { 839 throw new NullPointerException(); 840 } 841 if (!isRendered()) { 842 return; 843 } 844 String rendererType = getRendererType(); 845 if (rendererType != null) { 846 Renderer renderer = this.getRenderer(context); 847 if (renderer != null) { 848 renderer.encodeChildren(context, this); 849 } 850 // We've already logged for this component 851 } else { 852 if (getChildCount() > 0) { 853 for (UIComponent child : getChildren()) { 854 child.encodeAll(context); 855 } 856 } 857 } 858 } 859 860 861 /** 862 * @throws IOException {@inheritDoc} 863 * @throws NullPointerException {@inheritDoc} 864 */ 865 public void encodeEnd(FacesContext context) throws IOException { 866 867 if (context == null) { 868 throw new NullPointerException(); 869 } 870 if (!isRendered()) { 871 popComponentFromEL(context); 872 return; 873 } 874 String rendererType = getRendererType(); 875 if (rendererType != null) { 876 Renderer renderer = this.getRenderer(context); 877 if (renderer != null) { 878 renderer.encodeEnd(context, this); 879 } else { 880 // We've already logged for this component 881 } 882 } 883 popComponentFromEL(context); 884 885 } 886 887 // -------------------------------------------------- Event Listener Methods 888 889 private AttachedObjectListHolder<FacesListener> listeners; 890 891 /** 892 * <p>Add the specified {@link FacesListener} to the set of listeners 893 * registered to receive event notifications from this {@link UIComponent}. 894 * It is expected that {@link UIComponent} classes acting as event sources 895 * will have corresponding typesafe APIs for registering listeners of the 896 * required type, and the implementation of those registration methods 897 * will delegate to this method. For example:</p> 898 * <pre> 899 * public class FooEvent extends FacesEvent { 900 * ... 901 * protected boolean isAppropriateListener(FacesListener listener) { 902 * return (listener instanceof FooListener); 903 * } 904 * protected void processListener(FacesListener listener) { 905 * ((FooListener) listener).processFoo(this); 906 * } 907 * ... 908 * } 909 * <p/> 910 * public interface FooListener extends FacesListener { 911 * public void processFoo(FooEvent event); 912 * } 913 * <p/> 914 * public class FooComponent extends UIComponentBase { 915 * ... 916 * public void addFooListener(FooListener listener) { 917 * addFacesListener(listener); 918 * } 919 * public void removeFooListener(FooListener listener) { 920 * removeFacesListener(listener); 921 * } 922 * ... 923 * } 924 * </pre> 925 * 926 * @param listener The {@link FacesListener} to be registered 927 * @throws NullPointerException if <code>listener</code> 928 * is <code>null</code> 929 */ 930 protected void addFacesListener(FacesListener listener) { 931 932 if (listener == null) { 933 throw new NullPointerException(); 934 } 935 936 if (listeners == null) { 937 listeners = new AttachedObjectListHolder<FacesListener>(); 938 } 939 940 listeners.add(listener); 941 942 } 943 944 945 /** 946 * @throws IllegalArgumentException {@inheritDoc} 947 * @throws NullPointerException {@inheritDoc} 948 */ 949 protected FacesListener[] getFacesListeners(Class clazz) { 950 951 if (clazz == null) { 952 throw new NullPointerException(); 953 } 954 if (!FacesListener.class.isAssignableFrom(clazz)) { 955 throw new IllegalArgumentException(); 956 } 957 958 if (this.listeners == null) { 959 return (FacesListener[]) Array.newInstance(clazz, 0); 960 } 961 FacesListener[] listeners = this.listeners.asArray(FacesListener.class); 962 if (listeners.length == 0) { 963 return (FacesListener[]) Array.newInstance(clazz, 0); 964 } 965 966 List<FacesListener> results = new ArrayList<FacesListener>(listeners.length); 967 for (FacesListener listener : listeners) { 968 if (((Class<?>) clazz).isAssignableFrom(listener.getClass())) { 969 results.add(listener); 970 } 971 } 972 973 return (results.toArray 974 ((FacesListener[]) Array.newInstance(clazz, 975 results.size()))); 976 977 } 978 979 980 /** 981 * <p>Remove the specified {@link FacesListener} from the set of listeners 982 * registered to receive event notifications from this {@link UIComponent}. 983 * 984 * @param listener The {@link FacesListener} to be deregistered 985 * @throws NullPointerException if <code>listener</code> 986 * is <code>null</code> 987 */ 988 protected void removeFacesListener(FacesListener listener) { 989 990 if (listener == null) { 991 throw new NullPointerException(); 992 } 993 994 if (listeners != null) { 995 listeners.remove(listener); 996 } 997 998 } 999 1000 /** 1001 * @throws IllegalStateException {@inheritDoc} 1002 * @throws NullPointerException {@inheritDoc} 1003 */ 1004 public void queueEvent(FacesEvent event) { 1005 1006 if (event == null) { 1007 throw new NullPointerException(); 1008 } 1009 UIComponent parent = getParent(); 1010 if (parent == null) { 1011 throw new IllegalStateException(); 1012 } else { 1013 parent.queueEvent(event); 1014 } 1015 1016 } 1017 1018 1019 // ------------------------------------------------ Lifecycle Phase Handlers 1020 1021 1022 /** 1023 * @throws NullPointerException {@inheritDoc} 1024 */ 1025 public void processDecodes(FacesContext context) { 1026 1027 if (context == null) { 1028 throw new NullPointerException(); 1029 } 1030 1031 // Skip processing if our rendered flag is false 1032 if (!isRendered()) { 1033 return; 1034 } 1035 1036 pushComponentToEL(context, null); 1037 1038 // Process all facets and children of this component 1039 Iterator kids = getFacetsAndChildren(); 1040 while (kids.hasNext()) { 1041 UIComponent kid = (UIComponent) kids.next(); 1042 kid.processDecodes(context); 1043 } 1044 1045 // Process this component itself 1046 try { 1047 decode(context); 1048 } catch (RuntimeException e) { 1049 context.renderResponse(); 1050 throw e; 1051 } finally { 1052 popComponentFromEL(context); 1053 } 1054 1055 } 1056 1057 1058 /** 1059 * @throws NullPointerException {@inheritDoc} 1060 */ 1061 public void processValidators(FacesContext context) { 1062 1063 if (context == null) { 1064 throw new NullPointerException(); 1065 } 1066 1067 // Skip processing if our rendered flag is false 1068 if (!isRendered()) { 1069 return; 1070 } 1071 1072 pushComponentToEL(context, null); 1073 1074 Application app = context.getApplication(); 1075 app.publishEvent(context, PreValidateEvent.class, this); 1076 // Process all the facets and children of this component 1077 Iterator kids = getFacetsAndChildren(); 1078 while (kids.hasNext()) { 1079 UIComponent kid = (UIComponent) kids.next(); 1080 kid.processValidators(context); 1081 } 1082 app.publishEvent(context, PostValidateEvent.class, this); 1083 popComponentFromEL(context); 1084 1085 } 1086 1087 1088 /** 1089 * @throws NullPointerException {@inheritDoc} 1090 */ 1091 public void processUpdates(FacesContext context) { 1092 1093 if (context == null) { 1094 throw new NullPointerException(); 1095 } 1096 1097 // Skip processing if our rendered flag is false 1098 if (!isRendered()) { 1099 return; 1100 } 1101 1102 pushComponentToEL(context, null); 1103 1104 // Process all facets and children of this component 1105 Iterator kids = getFacetsAndChildren(); 1106 while (kids.hasNext()) { 1107 UIComponent kid = (UIComponent) kids.next(); 1108 kid.processUpdates(context); 1109 1110 } 1111 popComponentFromEL(context); 1112 1113 } 1114 1115 private static final int MY_STATE = 0; 1116 private static final int CHILD_STATE = 1; 1117 1118 /** 1119 * @throws NullPointerException {@inheritDoc} 1120 */ 1121 public Object processSaveState(FacesContext context) { 1122 1123 if (context == null) { 1124 throw new NullPointerException(); 1125 } 1126 if (this.isTransient()) { 1127 return null; 1128 } 1129 Object[] stateStruct = new Object[2]; 1130 Object[] childState = EMPTY_ARRAY; 1131 1132 pushComponentToEL(context, null); 1133 1134 // Process this component itself 1135 stateStruct[MY_STATE] = saveState(context); 1136 1137 // determine if we have any children to store 1138 int count = this.getChildCount() + this.getFacetCount(); 1139 if (count > 0) { 1140 1141 // this arraylist will store state 1142 List<Object> stateList = new ArrayList<Object>(count); 1143 1144 // if we have children, add them to the stateList 1145 if (this.getChildCount() > 0) { 1146 Iterator kids = getChildren().iterator(); 1147 UIComponent kid; 1148 while (kids.hasNext()) { 1149 kid = (UIComponent) kids.next(); 1150 if (!kid.isTransient()) { 1151 stateList.add(kid.processSaveState(context)); 1152 popComponentFromEL(context); 1153 } 1154 } 1155 } 1156 1157 pushComponentToEL(context, null); 1158 1159 // if we have facets, add them to the stateList 1160 if (this.getFacetCount() > 0) { 1161 Iterator myFacets = getFacets().entrySet().iterator(); 1162 UIComponent facet; 1163 Object facetState; 1164 Object[] facetSaveState; 1165 Map.Entry entry; 1166 while (myFacets.hasNext()) { 1167 entry = (Map.Entry) myFacets.next(); 1168 facet = (UIComponent) entry.getValue(); 1169 if (!facet.isTransient()) { 1170 facetState = facet.processSaveState(context); 1171 popComponentFromEL(context); 1172 facetSaveState = new Object[2]; 1173 facetSaveState[0] = entry.getKey(); 1174 facetSaveState[1] = facetState; 1175 stateList.add(facetSaveState); 1176 } 1177 } 1178 } 1179 1180 // finally, capture the stateList and replace the original, 1181 // EMPTY_OBJECT_ARRAY Object array 1182 childState = stateList.toArray(); 1183 } 1184 1185 stateStruct[CHILD_STATE] = childState; 1186 return stateStruct; 1187 } 1188 1189 /** 1190 * @throws NullPointerException {@inheritDoc} 1191 */ 1192 public void processRestoreState(FacesContext context, 1193 Object state) { 1194 if (context == null) { 1195 throw new NullPointerException(); 1196 } 1197 1198 Object[] stateStruct = (Object[]) state; 1199 Object[] childState = (Object[]) stateStruct[CHILD_STATE]; 1200 1201 // Process this component itself 1202 restoreState(context, stateStruct[MY_STATE]); 1203 1204 int i = 0; 1205 1206 // Process all the children of this component 1207 if (this.getChildCount() > 0) { 1208 for (UIComponent kid : getChildren()) { 1209 if (kid.isTransient()) { 1210 continue; 1211 } 1212 Object currentState = childState[i++]; 1213 if (currentState == null) { 1214 continue; 1215 } 1216 pushComponentToEL(context, null); 1217 kid.processRestoreState(context, currentState); 1218 popComponentFromEL(context); 1219 } 1220 } 1221 1222 // process all of the facets of this component 1223 if (this.getFacetCount() > 0) { 1224 int facetsSize = getFacets().size(); 1225 int j = 0; 1226 Object[] facetSaveState; 1227 String facetName; 1228 UIComponent facet; 1229 Object facetState; 1230 while (j < facetsSize) { 1231 if (null != (facetSaveState = (Object[]) childState[i++])) { 1232 facetName = (String) facetSaveState[0]; 1233 facetState = facetSaveState[1]; 1234 facet = getFacets().get(facetName); 1235 pushComponentToEL(context, null); 1236 facet.processRestoreState(context, facetState); 1237 popComponentFromEL(context); 1238 } 1239 ++j; 1240 } 1241 } 1242 } 1243 1244 // ------------------------------------------------------- Protected Methods 1245 1246 protected FacesContext getFacesContext() { 1247 1248 // PENDING(edburns): we can't use the cache ivar because we 1249 // don't always know when to clear it. For example, in the 1250 // "save state in server" case, the UIComponent instances stick 1251 // around between requests, yielding stale facesContext 1252 // references. If there was some way to clear the facesContext 1253 // cache ivar for each node in the tree *after* the 1254 // render-response phase, then we could keep a cache ivar. As 1255 // it is now, we must always use the Thread Local Storage 1256 // solution. 1257 1258 return FacesContext.getCurrentInstance(); 1259 1260 } 1261 1262 1263 protected Renderer getRenderer(FacesContext context) { 1264 1265 String rendererType = getRendererType(); 1266 Renderer result = null; 1267 if (rendererType != null) { 1268 result = context.getRenderKit().getRenderer(getFamily(), 1269 rendererType); 1270 if (null == result) { 1271 if (LOGGER.isLoggable(Level.FINE)) { 1272 LOGGER.fine("Can't get Renderer for type " + rendererType); 1273 } 1274 } 1275 } else { 1276 if (LOGGER.isLoggable(Level.FINE)) { 1277 String id = this.getId(); 1278 id = (null != id) ? id : this.getClass().getName(); 1279 LOGGER.fine("No renderer-type for component " + id); 1280 } 1281 } 1282 return result; 1283 } 1284 1285 1286 // ---------------------------------------------- PartialStateHolder Methods 1287 1288 /** 1289 * <p class="changed_added_2_0">For each of the attached objects on 1290 * this instance that implement {@link PartialStateHolder}, call 1291 * {@link PartialStateHolder#markInitialState} on the attached object.</p> 1292 * @since 2.0 1293 */ 1294 @Override 1295 public void markInitialState() { 1296 super.markInitialState(); 1297 1298 if (listeners != null) { 1299 listeners.markInitialState(); 1300 } 1301 if (listenersByEventClass != null) { 1302 for (List<SystemEventListener> listener : listenersByEventClass.values()) { 1303 if (listener instanceof PartialStateHolder) { 1304 ((PartialStateHolder) listener).markInitialState(); 1305 } 1306 } 1307 } 1308 if (behaviors != null) { 1309 for (Entry<String, List<ClientBehavior>> entry : behaviors.entrySet()) { 1310 for (ClientBehavior behavior : entry.getValue()) { 1311 if (behavior instanceof PartialStateHolder) { 1312 ((PartialStateHolder) behavior).markInitialState(); 1313 } 1314 } 1315 } 1316 } 1317 } 1318 1319 1320 /** 1321 * <p class="changed_added_2_0">For each of the attached objects on 1322 * this instance that implement {@link PartialStateHolder}, call 1323 * {@link PartialStateHolder#clearInitialState} on the attached object.</p> 1324 * @since 2.0 1325 */ 1326 @Override 1327 public void clearInitialState() { 1328 super.clearInitialState(); 1329 if (listeners != null) { 1330 listeners.clearInitialState(); 1331 } 1332 if (listenersByEventClass != null) { 1333 for (List<SystemEventListener> listener : listenersByEventClass.values()) { 1334 if (listener instanceof PartialStateHolder) { 1335 ((PartialStateHolder) listener).clearInitialState(); 1336 } 1337 } 1338 } 1339 if (behaviors != null) { 1340 for (Entry<String, List<ClientBehavior>> entry : behaviors.entrySet()) { 1341 for (ClientBehavior behavior : entry.getValue()) { 1342 if (behavior instanceof PartialStateHolder) { 1343 ((PartialStateHolder) behavior).clearInitialState(); 1344 } 1345 } 1346 } 1347 } 1348 } 1349 1350 private Object[] values; 1351 1352 public Object saveState(FacesContext context) { 1353 1354 if (context == null) { 1355 throw new NullPointerException(); 1356 } 1357 assert (!transientFlag); 1358 if (initialStateMarked()) { 1359 Object savedFacesListeners = ((listeners != null) ? listeners.saveState(context) : null); 1360 Object savedSysEventListeners = saveSystemEventListeners(context); 1361 Object savedBehaviors = saveBehaviorsState(context); 1362 Object savedBindings = null; 1363 if (bindings != null) { 1364 savedBindings = saveBindingsState(context); 1365 } 1366 Object savedHelper = null; 1367 if (stateHelper != null) { 1368 savedHelper = stateHelper.saveState(getFacesContext()); 1369 } 1370 if (savedFacesListeners == null 1371 && savedSysEventListeners == null 1372 && savedBehaviors == null 1373 && savedBindings == null 1374 && savedHelper == null) { 1375 return null; 1376 } else { 1377 if (values == null || values.length != 5) { 1378 values = new Object[5]; 1379 } 1380 1381 // since we're saving partial state, skip id and clientId 1382 // as this will be reconstructed from the template execution 1383 // when the view is restored 1384 values[0] = savedFacesListeners; 1385 values[1] = savedSysEventListeners; 1386 values[2] = savedBehaviors; 1387 values[3] = savedBindings; 1388 values[4] = savedHelper; 1389 return values; 1390 } 1391 } else { 1392 if (values == null || values.length != 6) { 1393 values = new Object[6]; 1394 } 1395 1396 values[0] = ((listeners != null) ? listeners.saveState(context) : null); 1397 values[1] = saveSystemEventListeners(context); 1398 values[2] = saveBehaviorsState(context); 1399 if (bindings != null) { 1400 values[3] = saveBindingsState(context); 1401 } 1402 if (stateHelper != null) { 1403 values[4] = stateHelper.saveState(getFacesContext()); 1404 } 1405 values[5] = id; 1406 1407 return (values); 1408 } 1409 } 1410 1411 1412 public void restoreState(FacesContext context, Object state) { 1413 1414 if (context == null) { 1415 throw new NullPointerException(); 1416 } 1417 1418 if (state == null) { 1419 return; 1420 } 1421 values = (Object[]) state; 1422 1423 if (values[0] != null) { 1424 if (listeners == null) { 1425 listeners = new AttachedObjectListHolder<FacesListener>(); 1426 } 1427 listeners.restoreState(context, values[0]); 1428 } 1429 if (values[1] != null) { 1430 Map m = restoreSystemEventListeners(context, values[1]); 1431 if (listenersByEventClass != null) { 1432 listenersByEventClass.putAll(m); 1433 } else { 1434 listenersByEventClass = m; 1435 } 1436 } 1437 if (values[2] != null) { 1438 behaviors = restoreBehaviorsState(context, values[2]); 1439 } 1440 if (values[3] != null) { 1441 bindings = restoreBindingsState(context, values[3]); 1442 } 1443 if(values[4] != null) { 1444 getStateHelper().restoreState(getFacesContext(), values[4]); 1445 } 1446 if (values.length == 6) { 1447 // this means we've saved full state and need to do a little more 1448 // work to finish the job 1449 if (values[5] != null) { 1450 id = (String) values[5]; 1451 } 1452 } 1453 1454 } 1455 1456 1457 /** 1458 * <p>Flag indicating a desire to now participate in state saving.</p> 1459 */ 1460 private boolean transientFlag = false; 1461 1462 public boolean isTransient() { 1463 1464 return (this.transientFlag); 1465 1466 } 1467 1468 1469 public void setTransient(boolean transientFlag) { 1470 1471 this.transientFlag = transientFlag; 1472 1473 } 1474 1475 // -------------------------------------- Helper methods for state saving 1476 1477 // --------- methods used by UIComponents to save their attached Objects. 1478 1479 /** 1480 * <p class="changed_modified_2_0">This method is called by {@link 1481 * UIComponent} subclasses that want to save one or more attached 1482 * objects. It is a convenience method that does the work of saving 1483 * attached objects that may or may not implement the {@link 1484 * StateHolder} interface. Using this method implies the use of 1485 * {@link #restoreAttachedState} to restore the attached 1486 * objects.</p> 1487 * <p/> 1488 * <p>This method supports saving attached objects of the following 1489 * type: <code>Object</code>s, <code>null</code> values, and <code 1490 * class="changed_modified_2_0">Collection</code>s of these objects. 1491 * If any contained objects are not <code 1492 * class="changed_modified_2_0">Collection</code>s and do not 1493 * implement {@link StateHolder}, they must have zero-argument 1494 * public constructors. The exact structure of the returned object 1495 * is undefined and opaque, but will be serializable. </p> 1496 * 1497 * @param context the {@link FacesContext} for this request. 1498 * @param attachedObject the object, which may be a 1499 * <code>List</code> instance, or an Object. The 1500 * <code>attachedObject</code> (or the elements that comprise 1501 * <code>attachedObject</code> may implement {@link StateHolder}. 1502 * @throws NullPointerException if the context argument is null. 1503 */ 1504 1505 public static Object saveAttachedState(FacesContext context, 1506 Object attachedObject) { 1507 if (null == context) { 1508 throw new NullPointerException(); 1509 } 1510 if (null == attachedObject) { 1511 return null; 1512 } 1513 Object result; 1514 1515 if (attachedObject instanceof Collection) { 1516 Collection attachedCollection = (Collection) attachedObject; 1517 List<StateHolderSaver> resultList = null; 1518 for (Object item : attachedCollection) { 1519 if (item != null) { 1520 if (item instanceof StateHolder && ((StateHolder) item).isTransient()) { 1521 continue; 1522 } 1523 if (resultList == null) { 1524 resultList = new ArrayList<StateHolderSaver>(attachedCollection.size() + 1); 1525 resultList.add(new StateHolderSaver(context, attachedCollection.getClass())); 1526 } 1527 resultList.add(new StateHolderSaver(context, item)); 1528 } 1529 } 1530 result = resultList; 1531 } else { 1532 result = new StateHolderSaver(context, attachedObject); 1533 } 1534 1535 return result; 1536 } 1537 1538 /** 1539 * <p>This method is called by {@link UIComponent} subclasses that 1540 * need to restore the objects they saved using {@link 1541 * #saveAttachedState}. This method is tightly coupled with {@link 1542 * #saveAttachedState}.</p> 1543 * <p/> 1544 * <p>This method supports restoring all attached objects types 1545 * supported by {@link #saveAttachedState}.</p> 1546 * 1547 * @param context the {@link FacesContext} for this request 1548 * @param stateObj the opaque object returned from {@link 1549 * #saveAttachedState} 1550 * @throws NullPointerException if context is null. 1551 * @throws IllegalStateException if the object is not 1552 * previously returned by {@link #saveAttachedState}. 1553 */ 1554 1555 public static Object restoreAttachedState(FacesContext context, 1556 Object stateObj) 1557 throws IllegalStateException { 1558 if (null == context) { 1559 throw new NullPointerException(); 1560 } 1561 if (null == stateObj) { 1562 return null; 1563 } 1564 Object result; 1565 1566 if (stateObj instanceof List) { 1567 List<StateHolderSaver> stateList = (List<StateHolderSaver>) stateObj; 1568 Collection<Object> retCollection = null; 1569 StateHolderSaver collectionSaver = stateList.get(0); 1570 Class collectionClass = (Class) collectionSaver.restore(context); 1571 try { 1572 retCollection = (Collection<Object>) collectionClass.newInstance(); 1573 } 1574 catch (Exception e) { 1575 if (LOGGER.isLoggable(Level.SEVERE)) { 1576 LOGGER.log(Level.SEVERE, e.toString(), e); 1577 } 1578 throw new IllegalStateException("Unknown object type"); 1579 } 1580 for (int i = 1, len = stateList.size(); i < len; i++) { 1581 try { 1582 retCollection.add(stateList.get(i).restore(context)); 1583 } catch (ClassCastException cce) { 1584 if (LOGGER.isLoggable(Level.SEVERE)) { 1585 LOGGER.log(Level.SEVERE, cce.toString(), cce); 1586 } 1587 throw new IllegalStateException("Unknown object type"); 1588 } 1589 } 1590 result = retCollection; 1591 } else if (stateObj instanceof StateHolderSaver) { 1592 StateHolderSaver saver = (StateHolderSaver) stateObj; 1593 result = saver.restore(context); 1594 } else { 1595 throw new IllegalStateException("Unknown object type"); 1596 } 1597 return result; 1598 } 1599 1600 private static Map<String, ValueExpression> restoreBindingsState(FacesContext context, Object state) { 1601 1602 if (state == null) { 1603 return (null); 1604 } 1605 Object values[] = (Object[]) state; 1606 String names[] = (String[]) values[0]; 1607 Object states[] = (Object[]) values[1]; 1608 Map<String, ValueExpression> bindings = new HashMap<String, ValueExpression>(names.length); 1609 for (int i = 0; i < names.length; i++) { 1610 bindings.put(names[i], 1611 (ValueExpression) restoreAttachedState(context, states[i])); 1612 } 1613 return (bindings); 1614 1615 } 1616 1617 1618 private Object saveBindingsState(FacesContext context) { 1619 1620 if (bindings == null) { 1621 return (null); 1622 } 1623 1624 Object values[] = new Object[2]; 1625 values[0] = bindings.keySet().toArray(new String[bindings.size()]); 1626 1627 Object[] bindingValues = bindings.values().toArray(); 1628 for (int i = 0; i < bindingValues.length; i++) { 1629 bindingValues[i] = saveAttachedState(context, bindingValues[i]); 1630 } 1631 1632 values[1] = bindingValues; 1633 1634 return (values); 1635 1636 } 1637 1638 private Object saveSystemEventListeners(FacesContext ctx) { 1639 1640 if (listenersByEventClass == null) { 1641 return null; 1642 } 1643 1644 int size = listenersByEventClass.size(); 1645 Object listeners[][] = new Object[size][2]; 1646 int idx = 0; 1647 boolean savedState = false; 1648 for (Entry<Class<? extends SystemEvent>, List<SystemEventListener>> e : listenersByEventClass.entrySet()) { 1649 Object[] target = listeners[idx++]; 1650 target[0] = e.getKey(); 1651 target[1] = saveAttachedState(ctx, e.getValue()); 1652 if (target[1] == null) { 1653 target[0] = null; 1654 } else { 1655 savedState = true; 1656 } 1657 } 1658 1659 return ((savedState) ? listeners : null); 1660 1661 } 1662 1663 1664 private Map<Class<? extends SystemEvent>, List<SystemEventListener>> restoreSystemEventListeners(FacesContext ctx, Object state) { 1665 1666 if (state == null) { 1667 return null; 1668 } 1669 1670 Object[][] listeners = (Object[][]) state; 1671 Map<Class<? extends SystemEvent>, List<SystemEventListener>> m = 1672 new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>(listeners.length, 1.0f); 1673 for (int i = 0, len = listeners.length; i < len; i++) { 1674 Object[] source = listeners[i]; 1675 m.put((Class<? extends SystemEvent>) source[0], 1676 (List<SystemEventListener>) restoreAttachedState(ctx, source[1])); 1677 } 1678 1679 return m; 1680 } 1681 1682 1683 Map<String, PropertyDescriptor> getDescriptorMap() { 1684 return pdMap; 1685 } 1686 1687 1688 private void doPostAddProcessing(FacesContext context, UIComponent added) { 1689 1690 if (parent.isInView()) { 1691 publishAfterViewEvents(context, context.getApplication(), added); 1692 } 1693 1694 } 1695 1696 private void doPreRemoveProcessing(FacesContext context, 1697 UIComponent toRemove) { 1698 1699 if (parent.isInView()) { 1700 disconnectFromView(context, context.getApplication(), toRemove); 1701 } 1702 1703 } 1704 1705 //------------------------------------------------------------- BehaviorHolder stub methods. 1706 1707 /** 1708 * behaviors associated with this component. 1709 */ 1710 private BehaviorsMap behaviors; 1711 1712 /** 1713 * <p class="changed_added_2_0">This is a default implementation of 1714 * {@link javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior}. 1715 * <code>UIComponent</code> does not implement the 1716 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface, 1717 * but provides default implementations for the methods defined by 1718 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify 1719 * subclass implementations. Subclasses that wish to support the 1720 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract must 1721 * declare that the subclass implements 1722 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must provide 1723 * an implementation of 1724 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p> 1725 * 1726 * @param eventName the logical name of the client-side event to attach 1727 * the behavior to. 1728 * @param behavior the {@link javax.faces.component.behavior.Behavior} 1729 * instance to attach for the specified event name. 1730 * 1731 * @since 2.0 1732 */ 1733 public void addClientBehavior(String eventName, ClientBehavior behavior) { 1734 assertClientBehaviorHolder(); 1735 // First, make sure that the event is supported. We don't want 1736 // to bother attaching behaviors for unsupported events. 1737 1738 Collection<String> eventNames = getEventNames(); 1739 1740 // getClientEventNames() is spec'ed to require a non-null Set. 1741 // If getClientEventNames() returns null, throw an exception 1742 // to indicate that the API in not being used properly. 1743 if (eventNames == null) { 1744 throw new IllegalStateException( 1745 "Attempting to add a Behavior to a component " + 1746 "that does not support any event types. " + 1747 "getEventTypes() must return a non-null Set."); 1748 } 1749 1750 if (eventNames.contains(eventName)) { 1751 1752 if (initialStateMarked()) { 1753 // a Behavior has been added dynamically. Update existing 1754 // Behaviors, if any, to save their full state. 1755 if (behaviors != null) { 1756 for (Entry<String, List<ClientBehavior>> entry : behaviors 1757 .entrySet()) { 1758 for (ClientBehavior b : entry.getValue()) { 1759 if (b instanceof PartialStateHolder) { 1760 ((PartialStateHolder) behavior).clearInitialState(); 1761 } 1762 } 1763 } 1764 } 1765 } 1766 // We've got an event that we support, create our Map 1767 // if necessary 1768 1769 if (null == behaviors) { 1770 // Typically we only have a small number of behaviors for 1771 // any component - in most cases only 1. Using a very small 1772 // initial capacity so that we keep the footprint to a minimum. 1773 Map<String, List<ClientBehavior>> modifiableMap = 1774 new HashMap<String, List<ClientBehavior>>(5,1.0f); 1775 behaviors = new BehaviorsMap(modifiableMap); 1776 } 1777 1778 List<ClientBehavior> eventBehaviours = behaviors.get(eventName); 1779 1780 if (null == eventBehaviours) { 1781 // Again using small initial capacity - we typically 1782 // only have 1 Behavior per event type. 1783 eventBehaviours = new ArrayList<ClientBehavior>(3); 1784 behaviors.getModifiableMap().put(eventName, eventBehaviours); 1785 } 1786 1787 eventBehaviours.add(behavior); 1788 } 1789 } 1790 1791 /** 1792 * <p class="changed_added_2_0">This is a default implementation of 1793 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}. 1794 * <code>UIComponent</code> does not implement the 1795 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface, 1796 * but provides default implementations for the methods defined by 1797 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify 1798 * subclass implementations. Subclasses that wish to support the 1799 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract 1800 * must declare that the subclass implements 1801 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must 1802 * override this method to return a non-Empty <code>Collection</code> 1803 * of the client event names that the component supports.</p> 1804 * 1805 * @since 2.0 1806 */ 1807 public Collection<String> getEventNames() { 1808 assertClientBehaviorHolder(); 1809 // Note: we intentionally return null here even though this 1810 // is not a valid value. The result is that addClientBehavior() 1811 // will fail with an IllegalStateException if getEventNames() 1812 // is not overridden. This should make it obvious to the 1813 // component author that something is wrong. 1814 return null; 1815 } 1816 1817 /** 1818 * <p class="changed_added_2_0">This is a default implementation of 1819 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors}. 1820 * <code>UIComponent</code> does not implement the 1821 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface, 1822 * but provides default implementations for the methods defined by 1823 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify 1824 * subclass implementations. Subclasses that wish to support the 1825 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract 1826 * must declare that the subclass implements 1827 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must add 1828 * an implementation of 1829 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p> 1830 * 1831 * @since 2.0 1832 */ 1833 public Map<String, List<ClientBehavior>> getClientBehaviors() { 1834 if (null == behaviors) { 1835 return Collections.emptyMap(); 1836 } 1837 1838 return behaviors; 1839 } 1840 1841 /** 1842 * <p class="changed_added_2_0">This is a default implementation of 1843 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName}. 1844 * <code>UIComponent</code> does not implement the 1845 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface, 1846 * but provides default implementations for the methods defined by 1847 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify 1848 * subclass implementations. Subclasses that wish to support the 1849 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract 1850 * must declare that the subclass implements 1851 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must 1852 * provide an implementation of 1853 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p> 1854 */ 1855 public String getDefaultEventName() { 1856 assertClientBehaviorHolder(); 1857 // Our default implementation just returns null - no default 1858 // event name; 1859 return null; 1860 } 1861 1862 /** 1863 * {@link UIComponentBase} has stub methods from the {@link ClientBehaviorHolder} interface, 1864 * but these method should be used only with componets that really implement holder interface. 1865 * For an any other classes this method throws {@link IllegalStateException} 1866 * @throws IllegalStateException 1867 */ 1868 private void assertClientBehaviorHolder() { 1869 if (!isClientBehaviorHolder()) { 1870 throw new IllegalStateException( 1871 "Attempting to use a Behavior feature with a component " + 1872 "that does not support any event types. " + 1873 "Component must implement BehaviourHolder interface."); 1874 } 1875 } 1876 1877 /** 1878 * @return true if component implements {@link ClientBehaviorHolder} interface. 1879 */ 1880 private boolean isClientBehaviorHolder() { 1881 return ClientBehaviorHolder.class.isInstance(this); 1882 } 1883 1884 /** 1885 * Save state of the behaviors map. 1886 * @param context the {@link FacesContext} for this request. 1887 * @return map converted to the array of <code>Object</code> or null if no behaviors have been set. 1888 */ 1889 private Object saveBehaviorsState(FacesContext context){ 1890 Object state = null; 1891 if (null != behaviors && behaviors.size() >0){ 1892 boolean stateWritten = false; 1893 Object[] attachedBehaviors = new Object[behaviors.size()]; 1894 int i = 0; 1895 for (List<ClientBehavior> eventBehaviors : behaviors.values()) { 1896 // we need to take different action depending on whether 1897 // or not markInitialState() was called. If it's not called, 1898 // assume JSF 1.2 style state saving and call through to 1899 // saveAttachedState(), otherwise, call saveState() on the 1900 // behaviors directly. 1901 Object[] attachedEventBehaviors = new Object[eventBehaviors.size()]; 1902 for (int j = 0; j < attachedEventBehaviors.length; j++) { 1903 attachedEventBehaviors[j] = ((initialStateMarked()) 1904 ? saveBehavior(context, eventBehaviors.get(j)) 1905 : saveAttachedState(context, eventBehaviors.get(j))); 1906 if (!stateWritten) { 1907 stateWritten = (attachedEventBehaviors[j] != null); 1908 } 1909 } 1910 attachedBehaviors[i++] = attachedEventBehaviors; 1911 } 1912 if (stateWritten) { 1913 state = new Object[]{behaviors.keySet().toArray(new String[behaviors.size()]),attachedBehaviors}; 1914 } 1915 } 1916 return state; 1917 } 1918 1919 /** 1920 * @param context the {@link FacesContext} for this request. 1921 * @param state saved state of the {@link Behavior}'s attached to the component. 1922 * @return restored <code>Map</code> of the behaviors. 1923 */ 1924 private BehaviorsMap restoreBehaviorsState(FacesContext context, Object state) { 1925 1926 if (null != state) { 1927 Object[] values = (Object[]) state; 1928 String[] names = (String[])values[0]; 1929 Object[] attachedBehaviors = (Object[]) values[1]; 1930 // we need to take different action depending on whether 1931 // or not markInitialState() was called. If it's not called, 1932 // assume JSF 1.2 style state saving and call through to 1933 // restoreAttachedState(), otherwise, call restoreState() on the 1934 // behaviors directly. 1935 if (!initialStateMarked()) { 1936 Map<String, List<ClientBehavior>> modifiableMap = new HashMap<String, List<ClientBehavior>>( 1937 names.length, 1938 1.0f); 1939 for (int i = 0; i < attachedBehaviors.length; i++) { 1940 Object[] attachedEventBehaviors = (Object[]) attachedBehaviors[i]; 1941 ArrayList<ClientBehavior> eventBehaviors = 1942 new ArrayList<ClientBehavior>(attachedBehaviors.length); 1943 for (int j = 0; j < attachedEventBehaviors.length; j++) { 1944 eventBehaviors.add((ClientBehavior) restoreAttachedState(context, 1945 attachedEventBehaviors[j])); 1946 } 1947 1948 modifiableMap.put(names[i], eventBehaviors); 1949 } 1950 1951 return new BehaviorsMap(modifiableMap); 1952 } else { 1953 for (int i = 0, len = names.length; i < len; i++) { 1954 // assume the behaviors have already been populated by 1955 // execution of the template. Process the state in the 1956 // same order that the names were saved. 1957 List<ClientBehavior> existingBehaviors = 1958 behaviors.get(names[i]); 1959 restoreBehaviors(context, existingBehaviors, (Object[]) attachedBehaviors[i]); 1960 } 1961 return behaviors; 1962 } 1963 } 1964 return null; 1965 } 1966 1967 private Object saveBehavior(FacesContext ctx, ClientBehavior behavior) { 1968 1969 // if the Behavior isn't a StateHolder, do nothing as it will be 1970 // added to the BehaviorMap when the template is re-executed. 1971 return ((behavior instanceof StateHolder) 1972 ? ((StateHolder) behavior).saveState(ctx) 1973 : null); 1974 1975 } 1976 1977 1978 private void restoreBehaviors(FacesContext ctx, List<ClientBehavior> existingBehaviors, Object[] state) { 1979 1980 // this method assumes a one to one correspondence in both length and 1981 // order. 1982 for (int i = 0, len = state.length; i < len; i++) { 1983 ClientBehavior behavior = existingBehaviors.get(i); 1984 if (state[i] == null) { 1985 // nothing to do...move on 1986 continue; 1987 } 1988 // if the Behavior is a StateHolder, invoke restoreState 1989 // passing in the current state. If it's not, just ignore 1990 // it and move along. 1991 if (behavior instanceof StateHolder) { 1992 ((StateHolder) behavior).restoreState(ctx, state[i]); 1993 } 1994 } 1995 } 1996 1997 1998 private static void publishAfterViewEvents(FacesContext context, 1999 Application application, 2000 UIComponent component) { 2001 2002 component.setInView(true); 2003 try { 2004 component.pushComponentToEL(context, component); 2005 application.publishEvent(context, PostAddToViewEvent.class, component); 2006 if (component.getChildCount() > 0) { 2007 Collection<UIComponent> clist = 2008 new ArrayList<UIComponent>(component.getChildren()); 2009 for (UIComponent c : clist) { 2010 publishAfterViewEvents(context, application, c); 2011 } 2012 } 2013 2014 if (component.getFacetCount() > 0) { 2015 Collection<UIComponent> clist = 2016 new ArrayList<UIComponent>(component.getFacets().values()); 2017 for (UIComponent c : clist) { 2018 publishAfterViewEvents(context, application, c); 2019 } 2020 } 2021 } finally { 2022 component.popComponentFromEL(context); 2023 } 2024 2025 } 2026 2027 2028 private static void disconnectFromView(FacesContext context, 2029 Application application, 2030 UIComponent component) { 2031 2032 application.publishEvent(context, 2033 PreRemoveFromViewEvent.class, 2034 component); 2035 component.setInView(false); 2036 component.compositeParent = null; 2037 if (component.getChildCount() > 0) { 2038 List<UIComponent> children = component.getChildren(); 2039 for (UIComponent c : children) { 2040 disconnectFromView(context, application, c); 2041 } 2042 } 2043 if (component.getFacetCount() > 0) { 2044 Map<String, UIComponent> facets = component.getFacets(); 2045 for (UIComponent c : facets.values()) { 2046 disconnectFromView(context, application, c); 2047 } 2048 } 2049 2050 } 2051 2052 2053 // --------------------------------------------------------- Private Classes 2054 2055 // For state saving 2056 private final static Object[] EMPTY_ARRAY = new Object[0]; 2057 2058 // Empty iterator for short circuiting operations 2059 private final static Iterator<UIComponent> EMPTY_ITERATOR = new Iterator<UIComponent>() { 2060 2061 public void remove() { 2062 throw new UnsupportedOperationException(); 2063 } 2064 2065 public UIComponent next() { 2066 throw new NoSuchElementException("Empty Iterator"); 2067 } 2068 2069 public boolean hasNext() { 2070 return false; 2071 } 2072 }; 2073 2074 // Private implementation of Map that supports the functionality 2075 // required by UIComponent.getFacets() 2076 // HISTORY: 2077 // Versions 1.333 and older used inheritence to provide the 2078 // basic map functionality. This was wasteful since a 2079 // component could be completely configured via ValueExpressions 2080 // or (Bindings) which means an EMPTY_OBJECT_ARRAY Map would always be 2081 // present when it wasn't needed. By using composition, 2082 // we control if and when the Map is instantiated thereby 2083 // reducing uneeded object allocation. This change also 2084 // has a nice side effect in state saving since we no 2085 // longer need to duplicate the map, we just provide the 2086 // private 'attributes' map directly to the state saving process. 2087 private static class AttributesMap implements Map<String, Object>, Serializable { 2088 2089 // this KEY is special to the AttributesMap - this allows the implementation 2090 // to access the the List containing the attributes that have been set 2091 private static final String ATTRIBUTES_THAT_ARE_SET_KEY = 2092 UIComponentBase.class.getName() + ".attributesThatAreSet"; 2093 2094 //private Map<String, Object> attributes; 2095 private transient Map<String, PropertyDescriptor> pdMap; 2096 private transient UIComponent component; 2097 private static final long serialVersionUID = -6773035086539772945L; 2098 2099 // -------------------------------------------------------- Constructors 2100 2101 private AttributesMap(UIComponent component) { 2102 2103 this.component = component; 2104 this.pdMap = ((UIComponentBase) component).getDescriptorMap(); 2105 2106 } 2107 2108 public boolean containsKey(Object keyObj) { 2109 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyObj)) { 2110 return true; 2111 } 2112 String key = (String) keyObj; 2113 PropertyDescriptor pd = 2114 getPropertyDescriptor(key); 2115 if (pd == null) { 2116 Map<String,Object> attributes = (Map<String,Object>) 2117 component.getStateHelper().get(PropertyKeys.attributes); 2118 if (attributes != null) { 2119 return attributes.containsKey(key); 2120 } else { 2121 return (false); 2122 } 2123 } else { 2124 return (false); 2125 } 2126 } 2127 2128 public Object get(Object keyObj) { 2129 String key = (String) keyObj; 2130 Object result = null; 2131 if (key == null) { 2132 throw new NullPointerException(); 2133 } 2134 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) { 2135 result = component.getStateHelper().get(UIComponent.PropertyKeysPrivate.attributesThatAreSet); 2136 } 2137 Map<String,Object> attributes = (Map<String,Object>) 2138 component.getStateHelper().get(PropertyKeys.attributes); 2139 if (null == result) { 2140 PropertyDescriptor pd = 2141 getPropertyDescriptor(key); 2142 if (pd != null) { 2143 try { 2144 Method readMethod = pd.getReadMethod(); 2145 if (readMethod != null) { 2146 result = (readMethod.invoke(component, 2147 EMPTY_OBJECT_ARRAY)); 2148 } else { 2149 throw new IllegalArgumentException(key); 2150 } 2151 } catch (IllegalAccessException e) { 2152 throw new FacesException(e); 2153 } catch (InvocationTargetException e) { 2154 throw new FacesException(e.getTargetException()); 2155 } 2156 } else if (attributes != null) { 2157 if (attributes.containsKey(key)) { 2158 result = attributes.get(key); 2159 } 2160 } 2161 } 2162 if (null == result) { 2163 ValueExpression ve = component.getValueExpression(key); 2164 if (ve != null) { 2165 try { 2166 result = ve.getValue(component.getFacesContext().getELContext()); 2167 } catch (ELException e) { 2168 throw new FacesException(e); 2169 } 2170 } 2171 } 2172 2173 return result; 2174 } 2175 2176 public Object put(String keyValue, Object value) { 2177 if (keyValue == null) { 2178 throw new NullPointerException(); 2179 } 2180 2181 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyValue)) { 2182 if (component.attributesThatAreSet == null) { 2183 if (value instanceof List) { 2184 component.getStateHelper().put(UIComponent.PropertyKeysPrivate.attributesThatAreSet, 2185 value); 2186 } 2187 } 2188 return null; 2189 } 2190 2191 PropertyDescriptor pd = 2192 getPropertyDescriptor(keyValue); 2193 if (pd != null) { 2194 try { 2195 Object result = null; 2196 Method readMethod = pd.getReadMethod(); 2197 if (readMethod != null) { 2198 result = readMethod.invoke 2199 (component, EMPTY_OBJECT_ARRAY); 2200 } 2201 Method writeMethod = pd.getWriteMethod(); 2202 if (writeMethod != null) { 2203 writeMethod.invoke 2204 (component, value); 2205 } else { 2206 // TODO: i18n 2207 throw new IllegalArgumentException("Setter not found for property " + keyValue); 2208 } 2209 return (result); 2210 } catch (IllegalAccessException e) { 2211 throw new FacesException(e); 2212 } catch (InvocationTargetException e) { 2213 throw new FacesException 2214 (e.getTargetException()); 2215 } 2216 } else { 2217 if (value == null) { 2218 throw new NullPointerException(); 2219 } 2220 2221 List<String> sProperties = 2222 (List<String>) component.getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet); 2223 if (sProperties == null) { 2224 component.getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, keyValue); 2225 } else if (!sProperties.contains(keyValue)) { 2226 component.getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, keyValue); 2227 } 2228 return putAttribute(keyValue, value); 2229 } 2230 } 2231 2232 public void putAll(Map<? extends String, ?> map) { 2233 if (map == null) { 2234 throw new NullPointerException(); 2235 } 2236 2237 for (Map.Entry<? extends String, ?> entry : map.entrySet()) { 2238 this.put(entry.getKey(), entry.getValue()); 2239 } 2240 } 2241 2242 public Object remove(Object keyObj) { 2243 String key = (String) keyObj; 2244 if (key == null) { 2245 throw new NullPointerException(); 2246 } 2247 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) { 2248 return null; 2249 } 2250 PropertyDescriptor pd = 2251 getPropertyDescriptor(key); 2252 if (pd != null) { 2253 throw new IllegalArgumentException(key); 2254 } else { 2255 Map<String,Object> attributes = getAttributes(); 2256 if (attributes != null) { 2257 component.getStateHelper().remove(UIComponent.PropertyKeysPrivate.attributesThatAreSet, 2258 key); 2259 return (component.getStateHelper().remove(PropertyKeys.attributes, 2260 key)); 2261 } else { 2262 return null; 2263 } 2264 } 2265 } 2266 2267 2268 public int size() { 2269 Map attributes = getAttributes(); 2270 return (attributes != null ? attributes.size() : 0); 2271 } 2272 2273 public boolean isEmpty() { 2274 Map attributes = getAttributes(); 2275 return (attributes == null || attributes.isEmpty()); 2276 } 2277 2278 public boolean containsValue(java.lang.Object value) { 2279 Map attributes= getAttributes(); 2280 return (attributes != null && attributes.containsValue(value)); 2281 } 2282 2283 public void clear() { 2284 component.getStateHelper().remove(PropertyKeys.attributes); 2285 component.getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet); 2286 } 2287 2288 public Set<String> keySet() { 2289 Map<String,Object> attributes = getAttributes(); 2290 if (attributes != null) 2291 return Collections.unmodifiableSet(attributes.keySet()); 2292 return Collections.emptySet(); 2293 } 2294 2295 public Collection<Object> values() { 2296 Map<String,Object> attributes = getAttributes(); 2297 if (attributes != null) 2298 return Collections.unmodifiableCollection(attributes.values()); 2299 return Collections.emptyList(); 2300 } 2301 2302 public Set<Entry<String, Object>> entrySet() { 2303 Map<String,Object> attributes = getAttributes(); 2304 if (attributes != null) 2305 return Collections.unmodifiableSet(attributes.entrySet()); 2306 return Collections.emptySet(); 2307 } 2308 2309 public boolean equals(Object o) { 2310 if (o == this) { 2311 return true; 2312 } 2313 2314 if (!(o instanceof Map)) { 2315 return false; 2316 } 2317 Map t = (Map) o; 2318 if (t.size() != size()) { 2319 return false; 2320 } 2321 2322 try { 2323 for (Object e : entrySet()) { 2324 Entry entry = (Entry) e; 2325 Object key = entry.getKey(); 2326 Object value = entry.getValue(); 2327 if (value == null) { 2328 if (!(t.get(key) == null && t.containsKey(key))) { 2329 return false; 2330 } 2331 } else { 2332 if (!value.equals(t.get(key))) { 2333 return false; 2334 } 2335 } 2336 } 2337 } catch (ClassCastException unused) { 2338 return false; 2339 } catch (NullPointerException unused) { 2340 return false; 2341 } 2342 2343 return true; 2344 } 2345 2346 2347 public int hashCode() { 2348 int h = 0; 2349 for (Object o : entrySet()) { 2350 h += o.hashCode(); 2351 } 2352 return h; 2353 } 2354 2355 private Map<String,Object> getAttributes() { 2356 return (Map<String,Object>) component.getStateHelper().get( 2357 PropertyKeys.attributes); 2358 } 2359 2360 private Object putAttribute(String key, Object value) { 2361 return component.getStateHelper().put(PropertyKeys.attributes, 2362 key, 2363 value); 2364 } 2365 2366 2367 /** 2368 * <p>Return the <code>PropertyDescriptor</code> for the specified 2369 * property name for this {@link UIComponent}'s implementation class, 2370 * if any; otherwise, return <code>null</code>.</p> 2371 * 2372 * @param name Name of the property to return a descriptor for 2373 * @throws FacesException if an introspection exception occurs 2374 */ 2375 PropertyDescriptor getPropertyDescriptor(String name) { 2376 if (pdMap != null) { 2377 return (pdMap.get(name)); 2378 } 2379 return (null); 2380 } 2381 2382 // ----------------------------------------------- Serialization Methods 2383 2384 // This is dependent on serialization occuring with in a 2385 // a Faces request, however, since UIComponentBase.{save,restore}State() 2386 // doesn't actually serialize the AttributesMap, these methods are here 2387 // purely to be good citizens. 2388 2389 private void writeObject(ObjectOutputStream out) throws IOException { 2390 out.writeObject(component.getClass()); 2391 //noinspection NonSerializableObjectPassedToObjectStream 2392 out.writeObject(component.saveState(FacesContext.getCurrentInstance())); 2393 } 2394 2395 private void readObject(ObjectInputStream in) 2396 throws IOException, ClassNotFoundException { 2397 //noinspection unchecked 2398 Class clazz = (Class) in.readObject(); 2399 try { 2400 component = (UIComponent) clazz.newInstance(); 2401 } catch (Exception e) { 2402 throw new RuntimeException(e); 2403 } 2404 component.restoreState(FacesContext.getCurrentInstance(), in.readObject()); 2405 } 2406 } 2407 2408 2409 // Private implementation of List that supports the functionality 2410 // required by UIComponent.getChildren() 2411 private static class ChildrenList extends ArrayList<UIComponent> { 2412 2413 private UIComponent component; 2414 2415 public ChildrenList(UIComponent component) { 2416 super(6); 2417 this.component = component; 2418 } 2419 2420 public void add(int index, UIComponent element) { 2421 if (element == null) { 2422 throw new NullPointerException(); 2423 } else if ((index < 0) || (index > size())) { 2424 throw new IndexOutOfBoundsException(); 2425 } else { 2426 eraseParent(element); 2427 super.add(index, element); 2428 element.setParent(component); 2429 2430 } 2431 } 2432 2433 public boolean add(UIComponent element) { 2434 if (element == null) { 2435 throw new NullPointerException(); 2436 } else { 2437 eraseParent(element); 2438 boolean result = super.add(element); 2439 element.setParent(component); 2440 return result; 2441 } 2442 } 2443 2444 public boolean addAll(Collection<? extends UIComponent> collection) { 2445 Iterator<UIComponent> elements = 2446 (new ArrayList<UIComponent>(collection)).iterator(); 2447 boolean changed = false; 2448 while (elements.hasNext()) { 2449 UIComponent element = elements.next(); 2450 if (element == null) { 2451 throw new NullPointerException(); 2452 } else { 2453 add(element); 2454 changed = true; 2455 } 2456 } 2457 return (changed); 2458 } 2459 2460 public boolean addAll(int index, Collection<? extends UIComponent> collection) { 2461 Iterator<UIComponent> elements = 2462 (new ArrayList<UIComponent>(collection)).iterator(); 2463 boolean changed = false; 2464 while (elements.hasNext()) { 2465 UIComponent element = elements.next(); 2466 if (element == null) { 2467 throw new NullPointerException(); 2468 } else { 2469 add(index++, element); 2470 changed = true; 2471 } 2472 } 2473 return (changed); 2474 } 2475 2476 public void clear() { 2477 int n = size(); 2478 if (n < 1) { 2479 return; 2480 } 2481 for (int i = 0; i < n; i++) { 2482 UIComponent child = get(i); 2483 child.setParent(null); 2484 } 2485 super.clear(); 2486 } 2487 2488 public Iterator<UIComponent> iterator() { 2489 return (new ChildrenListIterator(this)); 2490 } 2491 2492 public ListIterator<UIComponent> listIterator() { 2493 return (new ChildrenListIterator(this)); 2494 } 2495 2496 public ListIterator<UIComponent> listIterator(int index) { 2497 return (new ChildrenListIterator(this, index)); 2498 } 2499 2500 public UIComponent remove(int index) { 2501 UIComponent child = get(index); 2502 super.remove(index); 2503 child.setParent(null); 2504 return (child); 2505 } 2506 2507 public boolean remove(Object elementObj) { 2508 UIComponent element = (UIComponent) elementObj; 2509 if (element == null) { 2510 throw new NullPointerException(); 2511 } 2512 2513 if (super.remove(element)) { 2514 element.setParent(null); 2515 return (true); 2516 } else { 2517 return (false); 2518 } 2519 } 2520 2521 public boolean removeAll(Collection<?> collection) { 2522 boolean result = false; 2523 for (Object elements : collection) { 2524 if (remove(elements)) { 2525 result = true; 2526 } 2527 } 2528 return (result); 2529 } 2530 2531 public boolean retainAll(Collection<?> collection) { 2532 boolean modified = false; 2533 Iterator<?> items = iterator(); 2534 while (items.hasNext()) { 2535 if (!collection.contains(items.next())) { 2536 items.remove(); 2537 modified = true; 2538 } 2539 } 2540 return (modified); 2541 } 2542 2543 public UIComponent set(int index, UIComponent element) { 2544 if (element == null) { 2545 throw new NullPointerException(); 2546 } else if ((index < 0) || (index >= size())) { 2547 throw new IndexOutOfBoundsException(); 2548 } else { 2549 eraseParent(element); 2550 UIComponent previous = get(index); 2551 super.set(index, element); 2552 previous.setParent(null); 2553 element.setParent(component); 2554 return (previous); 2555 } 2556 } 2557 } 2558 2559 2560 // Private implementation of ListIterator for ChildrenList 2561 private static class ChildrenListIterator implements ListIterator<UIComponent> { 2562 2563 2564 public ChildrenListIterator(ChildrenList list) { 2565 this.list = list; 2566 this.index = 0; 2567 } 2568 2569 public ChildrenListIterator(ChildrenList list, int index) { 2570 this.list = list; 2571 if ((index < 0) || (index > list.size())) { 2572 throw new IndexOutOfBoundsException(String.valueOf(index)); 2573 } else { 2574 this.index = index; 2575 } 2576 } 2577 2578 2579 private ChildrenList list; 2580 private int index; 2581 private int last = -1; // Index last returned by next() or previous() 2582 2583 // Iterator methods 2584 2585 public boolean hasNext() { 2586 return (index < list.size()); 2587 } 2588 2589 public UIComponent next() { 2590 try { 2591 UIComponent o = list.get(index); 2592 last = index++; 2593 return (o); 2594 } catch (IndexOutOfBoundsException e) { 2595 throw new NoSuchElementException(String.valueOf(index)); 2596 } 2597 } 2598 2599 public void remove() { 2600 if (last == -1) { 2601 throw new IllegalStateException(); 2602 } 2603 list.remove(last); 2604 if (last < index) { 2605 index--; 2606 } 2607 last = -1; 2608 } 2609 2610 // ListIterator methods 2611 2612 public void add(UIComponent o) { 2613 last = -1; 2614 list.add(index++, o); 2615 } 2616 2617 public boolean hasPrevious() { 2618 return (index > 1); 2619 } 2620 2621 public int nextIndex() { 2622 return (index); 2623 } 2624 2625 public UIComponent previous() { 2626 try { 2627 int current = index - 1; 2628 UIComponent o = list.get(current); 2629 last = current; 2630 index = current; 2631 return (o); 2632 } catch (IndexOutOfBoundsException e) { 2633 throw new NoSuchElementException(); 2634 } 2635 } 2636 2637 public int previousIndex() { 2638 return (index - 1); 2639 } 2640 2641 public void set(UIComponent o) { 2642 if (last == -1) { 2643 throw new IllegalStateException(); 2644 } 2645 list.set(last, o); 2646 } 2647 2648 } 2649 2650 2651 // Private implementation of Iterator for getFacetsAndChildren() 2652 private final static class FacetsAndChildrenIterator implements Iterator<UIComponent> { 2653 2654 private Iterator<UIComponent> iterator; 2655 private boolean childMode; 2656 private UIComponent c; 2657 2658 public FacetsAndChildrenIterator(UIComponent c) { 2659 this.c = c; 2660 this.childMode = false; 2661 } 2662 2663 private void update() { 2664 if (this.iterator == null) { 2665 // we must guarantee that 'iterator' is never null 2666 if (this.c.getFacetCount() != 0) { 2667 this.iterator = this.c.getFacets().values().iterator(); 2668 this.childMode = false; 2669 } else if (this.c.getChildCount() != 0) { 2670 this.iterator = this.c.getChildren().iterator(); 2671 this.childMode = true; 2672 } else { 2673 this.iterator = EMPTY_ITERATOR; 2674 this.childMode = true; 2675 } 2676 } else if (!this.childMode 2677 && !this.iterator.hasNext() 2678 && this.c.getChildCount() != 0) { 2679 this.iterator = this.c.getChildren().iterator(); 2680 this.childMode = true; 2681 } 2682 } 2683 2684 public boolean hasNext() { 2685 this.update(); 2686 return this.iterator.hasNext(); 2687 } 2688 2689 public UIComponent next() { 2690 this.update(); 2691 return this.iterator.next(); 2692 } 2693 2694 public void remove() { 2695 throw new UnsupportedOperationException(); 2696 } 2697 2698 } 2699 2700 2701 // Private implementation of Map that supports the functionality 2702 // required by UIComponent.getFacets() 2703 private static class FacetsMap extends HashMap<String, UIComponent> { 2704 2705 private UIComponent component; 2706 2707 public FacetsMap(UIComponent component) { 2708 super(3, 1.0f); 2709 this.component = component; 2710 } 2711 2712 public void clear() { 2713 Iterator<String> keys = keySet().iterator(); 2714 while (keys.hasNext()) { 2715 keys.next(); 2716 keys.remove(); 2717 } 2718 super.clear(); 2719 } 2720 2721 public Set<Map.Entry<String, UIComponent>> entrySet() { 2722 return (new FacetsMapEntrySet(this)); 2723 } 2724 2725 public Set<String> keySet() { 2726 return (new FacetsMapKeySet(this)); 2727 } 2728 2729 public UIComponent put(String key, UIComponent value) { 2730 if ((key == null) || (value == null)) { 2731 throw new NullPointerException(); 2732 } else //noinspection ConstantConditions 2733 if (!(key instanceof String) || 2734 !(value instanceof UIComponent)) { 2735 throw new ClassCastException(); 2736 } 2737 UIComponent previous = super.get(key); 2738 if (previous != null) { 2739 previous.setParent(null); 2740 } 2741 eraseParent(value); 2742 UIComponent result = super.put(key, value); 2743 value.setParent(component); 2744 2745 return (result); 2746 } 2747 2748 public void putAll(Map<? extends String, ? extends UIComponent> map) { 2749 if (map == null) { 2750 throw new NullPointerException(); 2751 } 2752 for (Map.Entry<? extends String, ? extends UIComponent> entry : map.entrySet()) { 2753 put(entry.getKey(), entry.getValue()); 2754 } 2755 } 2756 2757 public UIComponent remove(Object key) { 2758 UIComponent previous = get(key); 2759 if (previous != null) { 2760 previous.setParent(null); 2761 } 2762 super.remove(key); 2763 return (previous); 2764 } 2765 2766 public Collection<UIComponent> values() { 2767 return (new FacetsMapValues(this)); 2768 } 2769 2770 Iterator<String> keySetIterator() { 2771 return ((new ArrayList<String>(super.keySet())).iterator()); 2772 } 2773 2774 } 2775 2776 2777 // Private implementation of Set for FacetsMap.getEntrySet() 2778 private static class FacetsMapEntrySet extends AbstractSet<Map.Entry<String, UIComponent>> { 2779 2780 public FacetsMapEntrySet(FacetsMap map) { 2781 this.map = map; 2782 } 2783 2784 private FacetsMap map = null; 2785 2786 public boolean add(Map.Entry<String, UIComponent> o) { 2787 throw new UnsupportedOperationException(); 2788 } 2789 2790 public boolean addAll(Collection<? extends Map.Entry<String, UIComponent>> c) { 2791 throw new UnsupportedOperationException(); 2792 } 2793 2794 public void clear() { 2795 map.clear(); 2796 } 2797 2798 public boolean contains(Object o) { 2799 if (o == null) { 2800 throw new NullPointerException(); 2801 } 2802 if (!(o instanceof Map.Entry)) { 2803 return (false); 2804 } 2805 Map.Entry e = (Map.Entry) o; 2806 Object k = e.getKey(); 2807 Object v = e.getValue(); 2808 if (!map.containsKey(k)) { 2809 return (false); 2810 } 2811 if (v == null) { 2812 return (map.get(k) == null); 2813 } else { 2814 return (v.equals(map.get(k))); 2815 } 2816 } 2817 2818 public boolean isEmpty() { 2819 return (map.isEmpty()); 2820 } 2821 2822 public Iterator<Map.Entry<String, UIComponent>> iterator() { 2823 return (new FacetsMapEntrySetIterator(map)); 2824 } 2825 2826 public boolean remove(Object o) { 2827 if (o == null) { 2828 throw new NullPointerException(); 2829 } 2830 if (!(o instanceof Map.Entry)) { 2831 return (false); 2832 } 2833 Object k = ((Map.Entry) o).getKey(); 2834 if (map.containsKey(k)) { 2835 map.remove(k); 2836 return (true); 2837 } else { 2838 return (false); 2839 } 2840 } 2841 2842 public boolean removeAll(Collection c) { 2843 boolean result = false; 2844 for (Object element : c) { 2845 if (remove(element)) { 2846 result = true; 2847 } 2848 } 2849 return (result); 2850 } 2851 2852 public boolean retainAll(Collection c) { 2853 boolean result = false; 2854 Iterator v = iterator(); 2855 while (v.hasNext()) { 2856 if (!c.contains(v.next())) { 2857 v.remove(); 2858 result = true; 2859 } 2860 } 2861 return (result); 2862 } 2863 2864 public int size() { 2865 return (map.size()); 2866 } 2867 2868 } 2869 2870 2871 // Private implementation of Map.Entry for FacetsMapEntrySet 2872 private static class FacetsMapEntrySetEntry implements Map.Entry<String, UIComponent> { 2873 2874 public FacetsMapEntrySetEntry(FacetsMap map, String key) { 2875 this.map = map; 2876 this.key = key; 2877 } 2878 2879 private FacetsMap map; 2880 private String key; 2881 2882 public boolean equals(Object o) { 2883 if (o == null) { 2884 return (false); 2885 } 2886 if (!(o instanceof Map.Entry)) { 2887 return (false); 2888 } 2889 Map.Entry e = (Map.Entry) o; 2890 if (key == null) { 2891 if (e.getKey() != null) { 2892 return (false); 2893 } 2894 } else { 2895 if (!key.equals(e.getKey())) { 2896 return (false); 2897 } 2898 } 2899 UIComponent v = map.get(key); 2900 if (v == null) { 2901 if (e.getValue() != null) { 2902 return (false); 2903 } 2904 } else { 2905 if (!v.equals(e.getValue())) { 2906 return (false); 2907 } 2908 } 2909 return (true); 2910 } 2911 2912 public String getKey() { 2913 return (key); 2914 } 2915 2916 public UIComponent getValue() { 2917 return (map.get(key)); 2918 } 2919 2920 public int hashCode() { 2921 Object value = map.get(key); 2922 return (((key == null) ? 0 : key.hashCode()) ^ 2923 ((value == null) ? 0 : value.hashCode())); 2924 } 2925 2926 public UIComponent setValue(UIComponent value) { 2927 UIComponent previous = map.get(key); 2928 map.put(key, value); 2929 return (previous); 2930 } 2931 2932 } 2933 2934 2935 // Private implementation of Set for FacetsMap.getEntrySet().iterator() 2936 private static class FacetsMapEntrySetIterator implements Iterator<Map.Entry<String, UIComponent>> { 2937 2938 public FacetsMapEntrySetIterator(FacetsMap map) { 2939 this.map = map; 2940 this.iterator = map.keySetIterator(); 2941 } 2942 2943 private FacetsMap map = null; 2944 private Iterator<String> iterator = null; 2945 private Map.Entry<String, UIComponent> last = null; 2946 2947 public boolean hasNext() { 2948 return (iterator.hasNext()); 2949 } 2950 2951 public Map.Entry<String, UIComponent> next() { 2952 last = new FacetsMapEntrySetEntry(map, iterator.next()); 2953 return (last); 2954 } 2955 2956 public void remove() { 2957 if (last == null) { 2958 throw new IllegalStateException(); 2959 } 2960 map.remove(((Map.Entry) last).getKey()); 2961 last = null; 2962 } 2963 2964 } 2965 2966 2967 // Private implementation of Set for FacetsMap.getKeySet() 2968 private static class FacetsMapKeySet extends AbstractSet<String> { 2969 2970 public FacetsMapKeySet(FacetsMap map) { 2971 this.map = map; 2972 } 2973 2974 private FacetsMap map = null; 2975 2976 public boolean add(String o) { 2977 throw new UnsupportedOperationException(); 2978 } 2979 2980 public boolean addAll(Collection<? extends String> c) { 2981 throw new UnsupportedOperationException(); 2982 } 2983 2984 public void clear() { 2985 map.clear(); 2986 } 2987 2988 public boolean contains(Object o) { 2989 return (map.containsKey(o)); 2990 } 2991 2992 public boolean containsAll(Collection c) { 2993 for (Object item : c) { 2994 if (!map.containsKey(item)) { 2995 return (false); 2996 } 2997 } 2998 return (true); 2999 } 3000 3001 public boolean isEmpty() { 3002 return (map.isEmpty()); 3003 } 3004 3005 public Iterator<String> iterator() { 3006 return (new FacetsMapKeySetIterator(map)); 3007 } 3008 3009 public boolean remove(Object o) { 3010 if (map.containsKey(o)) { 3011 map.remove(o); 3012 return (true); 3013 } else { 3014 return (false); 3015 } 3016 } 3017 3018 public boolean removeAll(Collection c) { 3019 boolean result = false; 3020 for (Object item : c) { 3021 if (map.containsKey(item)) { 3022 map.remove(item); 3023 result = true; 3024 } 3025 } 3026 return (result); 3027 } 3028 3029 public boolean retainAll(Collection c) { 3030 boolean result = false; 3031 Iterator v = iterator(); 3032 while (v.hasNext()) { 3033 if (!c.contains(v.next())) { 3034 v.remove(); 3035 result = true; 3036 } 3037 } 3038 return (result); 3039 } 3040 3041 public int size() { 3042 return (map.size()); 3043 } 3044 3045 } 3046 3047 3048 // Private implementation of Set for FacetsMap.getKeySet().iterator() 3049 private static class FacetsMapKeySetIterator implements Iterator<String> { 3050 3051 public FacetsMapKeySetIterator(FacetsMap map) { 3052 this.map = map; 3053 this.iterator = map.keySetIterator(); 3054 } 3055 3056 private FacetsMap map = null; 3057 private Iterator<String> iterator = null; 3058 private String last = null; 3059 3060 public boolean hasNext() { 3061 return (iterator.hasNext()); 3062 } 3063 3064 public String next() { 3065 last = iterator.next(); 3066 return (last); 3067 } 3068 3069 public void remove() { 3070 if (last == null) { 3071 throw new IllegalStateException(); 3072 } 3073 map.remove(last); 3074 last = null; 3075 } 3076 3077 } 3078 3079 3080 // Private implementation of Collection for FacetsMap.values() 3081 private static class FacetsMapValues extends AbstractCollection<UIComponent> { 3082 3083 public FacetsMapValues(FacetsMap map) { 3084 this.map = map; 3085 } 3086 3087 private FacetsMap map; 3088 3089 public boolean add(UIComponent o) { 3090 throw new UnsupportedOperationException(); 3091 } 3092 3093 public boolean addAll(Collection c) { 3094 throw new UnsupportedOperationException(); 3095 } 3096 3097 public void clear() { 3098 map.clear(); 3099 } 3100 3101 public boolean isEmpty() { 3102 return (map.isEmpty()); 3103 } 3104 3105 public Iterator<UIComponent> iterator() { 3106 return (new FacetsMapValuesIterator(map)); 3107 } 3108 3109 public int size() { 3110 return (map.size()); 3111 } 3112 3113 3114 } 3115 3116 3117 // Private implementation of Iterator for FacetsMap.values().iterator() 3118 private static class FacetsMapValuesIterator implements Iterator<UIComponent> { 3119 3120 public FacetsMapValuesIterator(FacetsMap map) { 3121 this.map = map; 3122 this.iterator = map.keySetIterator(); 3123 } 3124 3125 private FacetsMap map = null; 3126 private Iterator<String> iterator = null; 3127 private Object last = null; 3128 3129 public boolean hasNext() { 3130 return (iterator.hasNext()); 3131 } 3132 3133 public UIComponent next() { 3134 last = iterator.next(); 3135 return (map.get(last)); 3136 } 3137 3138 public void remove() { 3139 if (last == null) { 3140 throw new IllegalStateException(); 3141 } 3142 map.remove(last); 3143 last = null; 3144 } 3145 3146 } 3147 3148 // Private static member class that provide access to Behaviors. 3149 // Note that this Map must be unmodifiable to the external world, 3150 // but UIComponentBase itself needs to be able to write to the Map. 3151 // We solve these requirements wrapping the underlying modifiable 3152 // Map inside of a unmodifiable map and providing private access to 3153 // the underlying (modifable) Map 3154 private static class BehaviorsMap extends AbstractMap<String, List<ClientBehavior>>{ 3155 private Map<String, List<ClientBehavior>> unmodifiableMap; 3156 private Map<String, List<ClientBehavior>> modifiableMap; 3157 3158 private BehaviorsMap(Map<String, List<ClientBehavior>> modifiableMap) { 3159 this.modifiableMap = modifiableMap; 3160 this.unmodifiableMap = Collections.unmodifiableMap(modifiableMap); 3161 } 3162 3163 public Set<Entry<String, List<ClientBehavior>>> entrySet() { 3164 return unmodifiableMap.entrySet(); 3165 } 3166 3167 private Map<String, List<ClientBehavior>> getModifiableMap() { 3168 return modifiableMap; 3169 } 3170 } 3171 3172 }