Home » Mojarra-2.0.1 » javax » faces » component » [javadoc | source]

    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   }

Home » Mojarra-2.0.1 » javax » faces » component » [javadoc | source]