Save This Page
Home » mojarra-1.2_09-b02-FCS-source » 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   
   40   import javax.el.ELException;
   41   import javax.el.ValueExpression;
   42   import javax.faces.FacesException;
   43   import javax.faces.context.FacesContext;
   44   import javax.faces.el.ValueBinding;
   45   import javax.faces.event.AbortProcessingException;
   46   import javax.faces.event.FacesEvent;
   47   import javax.faces.event.FacesListener;
   48   import javax.faces.render.Renderer;
   49   
   50   import java.beans.IntrospectionException;
   51   import java.beans.Introspector;
   52   import java.beans.PropertyDescriptor;
   53   import java.io.IOException;
   54   import java.io.Serializable;
   55   import java.io.ObjectOutputStream;
   56   import java.io.ObjectInputStream;
   57   import java.lang.reflect.InvocationTargetException;
   58   import java.lang.reflect.Method;
   59   import java.util.AbstractCollection;
   60   import java.util.AbstractSet;
   61   import java.util.ArrayList;
   62   import java.util.Collection;
   63   import java.util.Collections;
   64   import java.util.HashMap;
   65   import java.util.Iterator;
   66   import java.util.List;
   67   import java.util.ListIterator;
   68   import java.util.Map;
   69   import java.util.NoSuchElementException;
   70   import java.util.Set;
   71   import java.util.WeakHashMap;
   72   import java.util.logging.Level;
   73   import java.util.logging.Logger;
   74   
   75   /**
   76    * <p><strong>UIComponentBase</strong> is a convenience base class that
   77    * implements the default concrete behavior of all methods defined by
   78    * {@link UIComponent}.</p>
   79    *
   80    * <p>By default, this class defines <code>getRendersChildren()</code>
   81    * to find the renderer for this component and call its
   82    * <code>getRendersChildren()</code> method.  The default implementation
   83    * on the <code>Renderer</code> returns <code>false</code>.  As of
   84    * version 1.2 of the JavaServer Faces Specification, component authors
   85    * are encouraged to return <code>true</code> from this method and rely
   86    * on the implementation of {@link #encodeChildren} in this class and in
   87    * the Renderer ({@link Renderer#encodeChildren}).  Subclasses that wish
   88    * to manage the rendering of their children should override this method
   89    * to return <code>true</code> instead.</p>
   90    */
   91   
   92   public abstract class UIComponentBase extends UIComponent {
   93   
   94   
   95       // -------------------------------------------------------------- Attributes
   96   
   97       private static Logger log = Logger.getLogger("javax.faces.component",
   98               "javax.faces.LogStrings");
   99   
  100   
  101       /**
  102        * <p>Each entry is an map of <code>PropertyDescriptor</code>s describing
  103        * the properties of a concrete {@link UIComponent} implementation, keyed
  104        * by the corresponding <code>java.lang.Class</code>.</p>
  105        *
  106        * <p><strong>IMPLEMENTATION NOTE</strong> - This is implemented as a
  107        * <code>WeakHashMap</code> so that, even if this class is embedded in a
  108        * container's class loader that is a parent to webapp class loaders,
  109        * references to the classes will eventually expire.</p>
  110        */
  111       @SuppressWarnings({"CollectionWithoutInitialCapacity"})
  112       private static Map<Class<?>, Map<String, PropertyDescriptor>>
  113             descriptors =
  114             new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>();
  115   
  116       /**
  117        * Reference to the map of <code>PropertyDescriptor</code>s for this class
  118        * in the <code>descriptors<code> <code>Map<code>.
  119        */
  120       private Map<String,PropertyDescriptor> pdMap = null;
  121   
  122       /**
  123        * <p>An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods.</p>
  124        */
  125       private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0];
  126   
  127       public UIComponentBase() {
  128           populateDescriptorsMapIfNecessary();
  129       }
  130   
  131       private void populateDescriptorsMapIfNecessary() {
  132           Class<?> clazz = this.getClass();
  133           pdMap = descriptors.get(clazz);
  134           if (null != pdMap) {
  135               return;
  136           }
  137   
  138           // load the property descriptors for this class.
  139           PropertyDescriptor pd[] = getPropertyDescriptors();
  140           if (pd != null) {
  141               pdMap = new HashMap<String, PropertyDescriptor>(pd.length, 1.0f);
  142               for (PropertyDescriptor aPd : pd) {
  143                   pdMap.put(aPd.getName(), aPd);
  144               }
  145               if (log.isLoggable(Level.FINE)) {
  146                   log.log(Level.FINE, "fine.component.populating_descriptor_map",
  147                           new Object[]{clazz,
  148                                        Thread.currentThread().getName()});
  149               }
  150   
  151               // Check again
  152               Map<String, PropertyDescriptor> reCheckMap =
  153                     descriptors.get(clazz);
  154               if (null != reCheckMap) {
  155                   return;
  156               }
  157               descriptors.put(clazz, pdMap);
  158           }
  159   
  160   
  161       }
  162   
  163   
  164       /**
  165        * <p>Return an array of <code>PropertyDescriptors</code> for this
  166        * {@link UIComponent}'s implementation class.  If no descriptors
  167        * can be identified, a zero-length array will be returned.</p>
  168        *
  169        * @throws FacesException if an introspection exception occurs
  170        */
  171       private PropertyDescriptor[] getPropertyDescriptors() {
  172           PropertyDescriptor[] pd;
  173           try {
  174               pd = Introspector.getBeanInfo(this.getClass()).
  175                   getPropertyDescriptors();
  176           } catch (IntrospectionException e) {
  177               throw new FacesException(e);
  178           }
  179           return (pd);
  180       }
  181   
  182   
  183       /**
  184        * <p>The <code>Map</code> containing our attributes, keyed by
  185        * attribute name.</p>
  186        */
  187       private AttributesMap attributes = null;
  188   
  189   
  190       public Map<String, Object> getAttributes() {
  191   
  192           if (attributes == null) {
  193               attributes = new AttributesMap(this);
  194           }
  195           return (attributes);
  196   
  197       }
  198   
  199   
  200       // ---------------------------------------------------------------- Bindings
  201   
  202   
  203       /**
  204        * {@inheritDoc}
  205        * @throws NullPointerException {@inheritDoc}
  206        * @deprecated This has been replaced by {@link #getValueExpression}.
  207        */
  208       public ValueBinding getValueBinding(String name) {
  209   
  210   	if (name == null) {
  211   	    throw new NullPointerException();
  212   	}
  213   	ValueBinding result = null;
  214   	ValueExpression ve;
  215   
  216   	if (null != (ve = getValueExpression(name))) {
  217   	    // if the ValueExpression is an instance of our private
  218   	    // wrapper class.
  219   	    if (ve.getClass().equals(ValueExpressionValueBindingAdapter.class)) {
  220   		result = ((ValueExpressionValueBindingAdapter)ve).getWrapped();
  221   	    }
  222   	    else {
  223   		// otherwise, this is a real ValueExpression.  Wrap it
  224   		// in a ValueBinding.
  225   		result = new ValueBindingValueExpressionAdapter(ve);
  226   	    }
  227   	}
  228   	return result;
  229       }
  230   
  231   
  232       /**
  233        * {@inheritDoc}
  234        * @throws IllegalArgumentException {@inheritDoc}
  235        * @throws NullPointerException {@inheritDoc}
  236        * @deprecated This has been replaced by {@link #setValueExpression}.
  237        */
  238       public void setValueBinding(String name, ValueBinding binding) {
  239   	if (name == null) {
  240   	    throw new NullPointerException();
  241   	}
  242   	if (binding != null) {
  243   	    ValueExpressionValueBindingAdapter adapter =
  244   		new ValueExpressionValueBindingAdapter(binding);
  245   	    setValueExpression(name, adapter);
  246   	} else {
  247   	    setValueExpression(name, null);
  248   	}
  249   
  250       }
  251   
  252   
  253       // -------------------------------------------------------------- Properties
  254   
  255   
  256       /**
  257        * <p>The assigned client identifier for this component.</p>
  258        */
  259       private String clientId = null;
  260   
  261   
  262       /**
  263        * @throws NullPointerException {@inheritDoc}
  264        */
  265       public String getClientId(FacesContext context) {
  266   
  267           if (context == null) {
  268               throw new NullPointerException();
  269           }
  270   
  271           // if the clientId is not yet set
  272           if (this.clientId == null) {
  273               UIComponent parent = this.getNamingContainer();
  274               String parentId = null;
  275   
  276               // give the parent the opportunity to first
  277               // grab a unique clientId
  278               if (parent != null) {
  279                   parentId = parent.getContainerClientId(context);
  280               }
  281   
  282               // now resolve our own client id
  283               this.clientId = getId();
  284               if (this.clientId == null) {
  285                   setId(context.getViewRoot().createUniqueId());
  286                   this.clientId = getId();
  287               }
  288               if (parentId != null) {
  289                   StringBuilder idBuilder =
  290                         new StringBuilder(parentId.length()
  291                                           + 1
  292                                           + this.clientId.length());
  293                   this.clientId = idBuilder.append(parentId)
  294                         .append(NamingContainer.SEPARATOR_CHAR)
  295                         .append(this.clientId).toString();
  296               }
  297   
  298               // allow the renderer to convert the clientId
  299               Renderer renderer = this.getRenderer(context);
  300               if (renderer != null) {
  301                   this.clientId = renderer.convertClientId(context, this.clientId);
  302               }
  303           }
  304           return this.clientId;
  305       }
  306   
  307       /**
  308        * <p>Private utilitity method for finding this
  309        * <code>UIComponent</code>'s parent <code>NamingContainer</code>.
  310        * This method may return <code>null</code> if there is not a
  311        * parent <code>NamingContainer</code></p>
  312        *
  313        * @return the parent <code>NamingContainer</code>
  314        */
  315       private UIComponent getNamingContainer() {
  316           UIComponent namingContainer = this.getParent();
  317           while (namingContainer != null) {
  318               if (namingContainer instanceof NamingContainer) {
  319                   return namingContainer;
  320               }
  321               namingContainer = namingContainer.getParent();
  322           }
  323           return null;
  324       }
  325   
  326   
  327   
  328       /**
  329        * <p>The component identifier for this component.</p>
  330        */
  331       private String id = null;
  332   
  333   
  334       public String getId() {
  335   
  336   	    return (id);
  337   
  338       }
  339   
  340   
  341       /**
  342        * @throws IllegalArgumentException {@inheritDoc}
  343        * @throws IllegalStateException {@inheritDoc}
  344        */
  345       public void setId(String id) {
  346   
  347           // if the current ID is not null, and the passed
  348           // argument is the same, no need to validate it
  349           // as it has already been validated.
  350           if (this.id == null || !(this.id.equals(id))) {
  351               validateId(id);
  352               this.id = id;
  353           }
  354           
  355           this.clientId = null; // Erase any cached value
  356   
  357       }
  358   
  359   
  360       /**
  361        * <p>The parent component for this component.</p>
  362        */
  363       private UIComponent parent = null;
  364   
  365   
  366       public UIComponent getParent() {
  367           return (this.parent);
  368       }
  369   
  370   
  371       public void setParent(UIComponent parent) {
  372           this.parent = parent;
  373       }
  374   
  375   
  376       /**
  377        * <p>The "should this component be rendered" flag.</p>
  378        */
  379       private boolean rendered = true;
  380       private boolean renderedSet = false;
  381   
  382       public boolean isRendered() {
  383   
  384           if (renderedSet) {
  385               return (rendered);
  386           }
  387           ValueExpression ve = getValueExpression("rendered");
  388           if (ve != null) {
  389               try {
  390                   return (!Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext())));
  391               }
  392               catch (ELException e) {
  393                   throw new FacesException(e);
  394               }
  395           } else {
  396               return (this.rendered);
  397           }
  398   
  399       }
  400   
  401   
  402       public void setRendered(boolean rendered) {
  403   
  404           this.rendered = rendered;
  405   	this.renderedSet = true;
  406   
  407       }
  408   
  409   
  410       /**
  411        * <p>The renderer type for this component.</p>
  412        */
  413       private String rendererType = null;
  414   
  415   
  416       public String getRendererType() {
  417   
  418           if (this.rendererType != null) {
  419               return (this.rendererType);
  420           }
  421           ValueExpression ve = getValueExpression("rendererType");
  422           if (ve != null) {
  423               try {
  424                   return ((String) ve.getValue(getFacesContext().getELContext()));
  425               }
  426               catch (ELException e) {
  427                   throw new FacesException(e);
  428               }
  429           } else {
  430               return (null);
  431           }
  432   
  433       }
  434   
  435   
  436       public void setRendererType(String rendererType) {
  437   
  438           this.rendererType = rendererType;
  439   
  440       }
  441   
  442   
  443       public boolean getRendersChildren() {
  444           boolean result = false;
  445   
  446           Renderer renderer;
  447           if (getRendererType() != null) {
  448               if (null !=
  449                   (renderer = getRenderer(getFacesContext()))) {
  450                   result = renderer.getRendersChildren();
  451               }
  452           }
  453           return result;
  454   
  455       }
  456   
  457       // ------------------------------------------------- Tree Management Methods
  458   
  459   
  460       /*
  461        * <p>The <code>List</code> containing our child components.</p>
  462        */
  463       private List<UIComponent> children = null;
  464   
  465   
  466       public List<UIComponent> getChildren() {
  467   
  468           if (children == null) {
  469               children = new ChildrenList(this);
  470           }
  471           return (children);
  472   
  473       }
  474   
  475   
  476       // Do not allocate the children List to answer this question
  477       public int getChildCount() {
  478   
  479           if (children != null) {
  480               return (children.size());
  481           } else {
  482               return (0);
  483           }
  484   
  485       }
  486   
  487   
  488       /**
  489        * <p>If the specified {@link UIComponent} has a non-null parent,
  490        * remove it as a child or facet (as appropriate) of that parent.
  491        * As a result, the <code>parent</code> property will always be
  492        * <code>null</code> when this method returns.</p>
  493        *
  494        * @param component {@link UIComponent} to have any parent erased
  495        */
  496       private static void eraseParent(UIComponent component) {
  497   
  498           UIComponent parent = component.getParent();
  499           if (parent == null) {
  500               return;
  501           }
  502           if (parent.getChildCount() > 0) {
  503               List children = parent.getChildren();
  504               int index = children.indexOf(component);
  505               if (index >= 0) {
  506                   children.remove(index);
  507                   return;
  508               }
  509           }
  510           if (parent.getFacetCount() > 0) {
  511               Map facets = parent.getFacets();
  512               Iterator entries = facets.entrySet().iterator();
  513               while (entries.hasNext()) {
  514                   Map.Entry entry = (Map.Entry) entries.next();
  515                   //noinspection ObjectEquality
  516                   if (entry.getValue() == component) {
  517                       entries.remove();
  518                       return;
  519                   }
  520               }
  521           }
  522   
  523           // Throw an exception for the "cannot happen" case
  524           throw new IllegalStateException("Parent was not null, " +
  525                                           "but this component not related");
  526   
  527       }
  528   
  529       /**
  530        * <p>Throw <code>IllegalArgumentException</code> if the specified
  531        * component identifier is non-<code>null</code> and not
  532        * syntactically valid.  </p>
  533        *
  534        * @param id The component identifier to test
  535        */
  536       private static void validateId(String id) {
  537   
  538           if (id == null) {
  539               return;
  540           }
  541           int n = id.length();
  542           if (n < 1) {
  543               throw new IllegalArgumentException();
  544           }
  545           for (int i = 0; i < n; i++) {
  546               char c = id.charAt(i);
  547               if (i == 0) {
  548                   if (!Character.isLetter(c) && (c != '_')) {
  549                       throw new IllegalArgumentException(id);
  550                   }
  551               } else {
  552                   if (!Character.isLetter(c) &&
  553                       !Character.isDigit(c) &&
  554                       (c != '-') && (c != '_')) {
  555                       throw new IllegalArgumentException(id);
  556                   }
  557               }
  558           }
  559   
  560       }
  561   
  562   
  563       private static final String SEPARATOR_STRING =
  564           String.valueOf(NamingContainer.SEPARATOR_CHAR);
  565   
  566       /**
  567        * @throws NullPointerException {@inheritDoc}
  568        */
  569       public UIComponent findComponent(String expr) {
  570           if (expr == null) {
  571               throw new NullPointerException();
  572           }
  573   
  574           if (expr.length() == 0) {
  575               // if an empty value is provided, fail fast.
  576               throw new IllegalArgumentException("\"\"");
  577           }
  578   
  579           // Identify the base component from which we will perform our search
  580           UIComponent base = this;
  581           if (expr.charAt(0) == NamingContainer.SEPARATOR_CHAR) {
  582               // Absolute searches start at the root of the tree
  583               while (base.getParent() != null) {
  584                   base = base.getParent();
  585               }
  586               // Treat remainder of the expression as relative
  587               expr = expr.substring(1);
  588           } else {
  589               // Relative expressions start at the closest NamingContainer or root
  590               while (base.getParent() != null) {
  591                   if (base instanceof NamingContainer) {
  592                       break;
  593                   }
  594                   base = base.getParent();
  595               }
  596           }
  597   
  598           // Evaluate the search expression (now guaranteed to be relative)
  599           UIComponent result = null;
  600           String[] segments = expr.split(SEPARATOR_STRING);
  601           for (int i = 0, length = (segments.length - 1);
  602                i < segments.length;
  603                i++, length--) {
  604               result = findComponent(base, segments[i], (i == 0));
  605               // the first element of the expression may match base.id
  606               // (vs. a child if of base)
  607               if (i == 0 && result == null &&
  608                    segments[i].equals(base.getId())) {
  609                   result = base;
  610               }
  611               if (result != null && (!(result instanceof NamingContainer)) && length > 0) {
  612                   throw new IllegalArgumentException(segments[i]);
  613               }
  614               if (result == null) {
  615                   break;
  616               }
  617               base = result;
  618           }
  619   
  620           // Return the final result of our search
  621           return (result);
  622   
  623       }
  624   
  625   
  626       /**
  627        * <p>Return the {@link UIComponent} (if any) with the specified
  628        * <code>id</code>, searching recursively starting at the specified
  629        * <code>base</code>, and examining the base component itself, followed
  630        * by examining all the base component's facets and children (unless
  631        * the base component is a {@link NamingContainer}, in which case the
  632        * recursive scan is skipped.</p>
  633        *
  634        * @param base Base {@link UIComponent} from which to search
  635        * @param id Component identifier to be matched
  636        */
  637       private static UIComponent findComponent(UIComponent base,
  638                                                String id,
  639                                                boolean checkId) {
  640           if (checkId && id.equals(base.getId())) {
  641               return base;
  642           }
  643           // Search through our facets and children
  644           UIComponent result = null;
  645           for (Iterator i = base.getFacetsAndChildren(); i.hasNext(); ) {
  646               UIComponent kid = (UIComponent) i.next();
  647               if (!(kid instanceof NamingContainer)) {
  648                   if (checkId && id.equals(kid.getId())) {
  649                       result = kid;
  650                       break;
  651                   }
  652                   result = findComponent(kid, id, true);
  653                   if (result != null) {
  654                       break;
  655                   }
  656               } else if (id.equals(kid.getId())) {
  657                   result = kid;
  658                   break;
  659               }
  660           }
  661           return (result);
  662   
  663       }
  664   
  665       /**
  666        * {@inheritDoc}
  667        * @since 1.2
  668        * @throws NullPointerException {@inheritDoc}
  669        * @throws FacesException {@inheritDoc}
  670        *
  671        */
  672       public boolean invokeOnComponent(FacesContext context, String clientId,
  673   				     ContextCallback callback)
  674   	throws FacesException {
  675           return super.invokeOnComponent(context, clientId, callback);
  676       }
  677   
  678   
  679       // ------------------------------------------------ Facet Management Methods
  680   
  681   
  682       /*
  683        * <p>The <code>Map</code> containing our related facet components.</p>
  684        */
  685       private Map<String, UIComponent> facets = null;
  686   
  687   
  688       public Map<String, UIComponent> getFacets() {
  689   
  690           if (facets == null) {
  691               facets = new FacetsMap(this);
  692           }
  693           return (facets);
  694   
  695       }
  696   
  697       // Do not allocate the children List to answer this question
  698       public int getFacetCount() {
  699   
  700           if (facets != null) {
  701               return (facets.size());
  702           } else {
  703               return (0);
  704           }
  705   
  706       }
  707   
  708   
  709       // Do not allocate the facets Map to answer this question
  710       public UIComponent getFacet(String name) {
  711   
  712           if (facets != null) {
  713               return (facets.get(name));
  714           } else {
  715               return (null);
  716           }
  717   
  718       }
  719   
  720   
  721       public Iterator<UIComponent> getFacetsAndChildren() {
  722   
  723           Iterator<UIComponent> result;
  724           int childCount = this.getChildCount(),
  725                   facetCount = this.getFacetCount();
  726           // If there are neither facets nor children
  727           if (0 == childCount && 0 == facetCount) {
  728               result = EMPTY_ITERATOR;
  729           }
  730           // If there are only facets and no children
  731           else if (0 == childCount) {
  732               Collection<UIComponent> unmodifiable =
  733                 Collections.unmodifiableCollection(getFacets().values());
  734               result = unmodifiable.iterator();
  735           }
  736           // If there are only children and no facets
  737           else if (0 == facetCount) {
  738               List<UIComponent> unmodifiable =
  739                 Collections.unmodifiableList(getChildren());
  740               result = unmodifiable.iterator();
  741           }
  742           // If there are both children and facets
  743           else {
  744               result = new FacetsAndChildrenIterator(this);
  745           }
  746           return result;
  747       }
  748   
  749   
  750       // -------------------------------------------- Lifecycle Processing Methods
  751   
  752       /**
  753        * @throws AbortProcessingException {@inheritDoc}
  754        * @throws IllegalStateException {@inheritDoc}
  755        * @throws NullPointerException {@inheritDoc}
  756        */
  757       public void broadcast(FacesEvent event)
  758           throws AbortProcessingException {
  759   
  760           if (event == null) {
  761               throw new NullPointerException();
  762           }
  763           if (listeners == null) {
  764               return;
  765           }
  766   
  767           Iterator<FacesListener> iter = listeners.iterator();
  768           while (iter.hasNext()) {
  769               FacesListener listener = iter.next();
  770               if (event.isAppropriateListener(listener)) {
  771                   event.processListener(listener);
  772               }
  773           }
  774       }
  775   
  776   
  777       /**
  778        * @throws NullPointerException {@inheritDoc}
  779        */
  780       public void decode(FacesContext context) {
  781   
  782           if (context == null) {
  783               throw new NullPointerException();
  784           }
  785           String rendererType = getRendererType();
  786           if (rendererType != null) {
  787               Renderer renderer = this.getRenderer(context);
  788               if (renderer != null) {
  789                   renderer.decode(context, this);
  790               }else {
  791                   // TODO: i18n
  792                   log.fine("Can't get Renderer for type " + rendererType);
  793               }
  794           }
  795       }
  796   
  797   
  798       /**
  799        * @throws NullPointerException {@inheritDoc}
  800        */
  801       public void encodeBegin(FacesContext context) throws IOException {
  802   
  803           if (context == null) {
  804               throw new NullPointerException();
  805           }
  806           if (!isRendered()) {
  807               return;
  808           }
  809           String rendererType = getRendererType();
  810           if (rendererType != null) {
  811               Renderer renderer = this.getRenderer(context);
  812               if (renderer != null) {
  813                   renderer.encodeBegin(context, this);
  814               } else {
  815                   // TODO: i18n
  816                   log.fine("Can't get Renderer for type " + rendererType);
  817               }
  818           }
  819   
  820       }
  821   
  822       /**
  823        * @throws NullPointerException {@inheritDoc}
  824        */
  825       public void encodeChildren(FacesContext context) throws IOException {
  826   
  827           if (context == null) {
  828               throw new NullPointerException();
  829           }
  830           if (!isRendered()) {
  831               return;
  832           }
  833           String rendererType = getRendererType();
  834           if (rendererType != null) {
  835               Renderer renderer = this.getRenderer(context);
  836               if (renderer != null) {
  837                   renderer.encodeChildren(context, this);
  838               } else {
  839                   // We've already logged for this component
  840               }
  841           }
  842       }
  843   
  844   
  845       /**
  846        * @throws IOException {@inheritDoc}
  847        * @throws NullPointerException {@inheritDoc}
  848        */
  849       public void encodeEnd(FacesContext context) throws IOException {
  850   
  851           if (context == null) {
  852               throw new NullPointerException();
  853           }
  854           if (!isRendered()) {
  855               return;
  856           }
  857           String rendererType = getRendererType();
  858           if (rendererType != null) {
  859               Renderer renderer = this.getRenderer(context);
  860               if (renderer != null) {
  861                   renderer.encodeEnd(context, this);
  862               } else {
  863                   // We've already logged for this component
  864               }
  865           }
  866   
  867       }
  868   
  869       // -------------------------------------------------- Event Listener Methods
  870   
  871   
  872       /**
  873        * <p>Our {@link javax.faces.event.FacesListener}s.  This data
  874        * structure is lazily instantiated as necessary.</p>
  875        */
  876       private List<FacesListener> listeners;
  877   
  878   
  879       /**
  880        * <p>Add the specified {@link FacesListener} to the set of listeners
  881        * registered to receive event notifications from this {@link UIComponent}.
  882        * It is expected that {@link UIComponent} classes acting as event sources
  883        * will have corresponding typesafe APIs for registering listeners of the
  884        * required type, and the implementation of those registration methods
  885        * will delegate to this method.  For example:</p>
  886        * <pre>
  887        * public class FooEvent extends FacesEvent {
  888        *   ...
  889        *   protected boolean isAppropriateListener(FacesListener listener) {
  890        *     return (listener instanceof FooListener);
  891        *   }
  892        *   protected void processListener(FacesListener listener) {
  893        *     ((FooListener) listener).processFoo(this);
  894        *   }
  895        *   ...
  896        * }
  897        *
  898        * public interface FooListener extends FacesListener {
  899        *   public void processFoo(FooEvent event);
  900        * }
  901        *
  902        * public class FooComponent extends UIComponentBase {
  903        *   ...
  904        *   public void addFooListener(FooListener listener) {
  905        *     addFacesListener(listener);
  906        *   }
  907        *   public void removeFooListener(FooListener listener) {
  908        *     removeFacesListener(listener);
  909        *   }
  910        *   ...
  911        * }
  912        * </pre>
  913        *
  914        * @param listener The {@link FacesListener} to be registered
  915        *
  916        * @throws NullPointerException if <code>listener</code>
  917        *  is <code>null</code>
  918        */
  919       protected void addFacesListener(FacesListener listener) {
  920   
  921           if (listener == null) {
  922               throw new NullPointerException();
  923           }
  924           if (listeners == null) {
  925               //noinspection CollectionWithoutInitialCapacity
  926               listeners = new ArrayList<FacesListener>();
  927           }
  928           listeners.add(listener);
  929   
  930       }
  931   
  932   
  933       /**
  934        * @throws IllegalArgumentException {@inheritDoc}
  935        * @throws NullPointerException {@inheritDoc}
  936        */
  937       protected FacesListener[] getFacesListeners(Class clazz) {
  938           if (clazz == null) {
  939               throw new NullPointerException();
  940           }
  941           if (!FacesListener.class.isAssignableFrom(clazz)) {
  942               throw new IllegalArgumentException();
  943           }
  944           if (listeners == null) {
  945               return ((FacesListener[])
  946                   java.lang.reflect.Array.newInstance(clazz, 0));
  947           }
  948   
  949           //noinspection CollectionWithoutInitialCapacity
  950           List<FacesListener> results = new ArrayList<FacesListener>();
  951   	Iterator<FacesListener> items = listeners.iterator();
  952   	while (items.hasNext()) {
  953   	    FacesListener item = items.next();
  954   	    if (((Class<?>)clazz).isAssignableFrom(item.getClass())) {
  955   		results.add(item);
  956   	    }
  957   	}
  958   
  959           return (results.toArray
  960                   ((FacesListener []) java.lang.reflect.Array.newInstance(clazz,
  961   								 results.size())));
  962   
  963       }
  964   
  965   
  966       /**
  967        * <p>Remove the specified {@link FacesListener} from the set of listeners
  968        * registered to receive event notifications from this {@link UIComponent}.
  969        *
  970        * @param listener The {@link FacesListener} to be deregistered
  971        *
  972        * @throws NullPointerException if <code>listener</code>
  973        *  is <code>null</code>
  974        */
  975       protected void removeFacesListener(FacesListener listener) {
  976   
  977           if (listener == null) {
  978               throw new NullPointerException();
  979           }
  980           if (listeners == null) {
  981               return;
  982           }
  983   	listeners.remove(listener);
  984       }
  985   
  986       /**
  987        * @throws IllegalStateException {@inheritDoc}
  988        * @throws NullPointerException {@inheritDoc}
  989        */
  990       public void queueEvent(FacesEvent event) {
  991   
  992           if (event == null) {
  993               throw new NullPointerException();
  994           }
  995           UIComponent parent = getParent();
  996           if (parent == null) {
  997               throw new IllegalStateException();
  998           } else {
  999               parent.queueEvent(event);
 1000           }
 1001   
 1002       }
 1003   
 1004   
 1005       // ------------------------------------------------ Lifecycle Phase Handlers
 1006   
 1007   
 1008       /**
 1009        * @throws NullPointerException {@inheritDoc}
 1010        */
 1011       public void processDecodes(FacesContext context) {
 1012   
 1013           if (context == null) {
 1014               throw new NullPointerException();
 1015           }
 1016   
 1017           // Skip processing if our rendered flag is false
 1018           if (!isRendered()) {
 1019               return;
 1020           }
 1021   
 1022           // Process all facets and children of this component
 1023           Iterator kids = getFacetsAndChildren();
 1024           while (kids.hasNext()) {
 1025               UIComponent kid = (UIComponent) kids.next();
 1026               kid.processDecodes(context);
 1027           }
 1028   
 1029           // Process this component itself
 1030           try {
 1031               decode(context);
 1032           } catch (RuntimeException e) {
 1033               context.renderResponse();
 1034               throw e;
 1035           }
 1036   
 1037       }
 1038   
 1039   
 1040       /**
 1041        * @throws NullPointerException {@inheritDoc}
 1042        */
 1043       public void processValidators(FacesContext context) {
 1044   
 1045           if (context == null) {
 1046               throw new NullPointerException();
 1047           }
 1048   
 1049           // Skip processing if our rendered flag is false
 1050           if (!isRendered()) {
 1051               return;
 1052           }
 1053   
 1054           // Process all the facets and children of this component
 1055           Iterator kids = getFacetsAndChildren();
 1056           while (kids.hasNext()) {
 1057               UIComponent kid = (UIComponent) kids.next();
 1058               kid.processValidators(context);
 1059           }
 1060       }
 1061   
 1062   
 1063       /**
 1064        * @throws NullPointerException {@inheritDoc}
 1065        */
 1066       public void processUpdates(FacesContext context) {
 1067   
 1068           if (context == null) {
 1069               throw new NullPointerException();
 1070           }
 1071   
 1072           // Skip processing if our rendered flag is false
 1073           if (!isRendered()) {
 1074               return;
 1075           }
 1076   
 1077           // Process all facets and children of this component
 1078           Iterator kids = getFacetsAndChildren();
 1079           while (kids.hasNext()) {
 1080               UIComponent kid = (UIComponent) kids.next();
 1081               kid.processUpdates(context);
 1082           }
 1083       }
 1084   
 1085       private static final int MY_STATE = 0;
 1086       private static final int CHILD_STATE = 1;
 1087   
 1088       /**
 1089        * @throws NullPointerException {@inheritDoc}
 1090        */
 1091       public Object processSaveState(FacesContext context) {
 1092   
 1093           if (context == null) {
 1094               throw new NullPointerException();
 1095           }
 1096           if (this.isTransient()) {
 1097               return null;
 1098           }
 1099           Object [] stateStruct = new Object[2];
 1100           Object [] childState = EMPTY_ARRAY;
 1101   
 1102           // Process this component itself
 1103           stateStruct[MY_STATE] = saveState(context);
 1104   
 1105           // determine if we have any children to store
 1106           int count = this.getChildCount() + this.getFacetCount();
 1107           if (count > 0) {
 1108   
 1109               // this arraylist will store state
 1110               List<Object> stateList = new ArrayList<Object>(count);
 1111   
 1112               // if we have children, add them to the stateList
 1113               if (this.getChildCount() > 0) {
 1114                   Iterator kids = getChildren().iterator();
 1115                   UIComponent kid;
 1116                   while (kids.hasNext()) {
 1117                       kid = (UIComponent) kids.next();
 1118                       if (!kid.isTransient()) {
 1119                           stateList.add(kid.processSaveState(context));
 1120                       }
 1121                   }
 1122               }
 1123   
 1124               // if we have facets, add them to the stateList
 1125               if (this.getFacetCount() > 0) {
 1126                   Iterator myFacets = getFacets().entrySet().iterator();
 1127                   UIComponent facet;
 1128                   Object facetState;
 1129                   Object[] facetSaveState;
 1130                   Map.Entry entry;
 1131                   while (myFacets.hasNext()) {
 1132                       entry = (Map.Entry) myFacets.next();
 1133                       facet = (UIComponent) entry.getValue();
 1134                       if (!facet.isTransient()) {
 1135                           facetState = facet.processSaveState(context);
 1136                           facetSaveState = new Object[2];
 1137                           facetSaveState[0] = entry.getKey();
 1138                           facetSaveState[1] = facetState;
 1139                           stateList.add(facetSaveState);
 1140                       }
 1141                   }
 1142               }
 1143   
 1144               // finally, capture the stateList and replace the original,
 1145               // EMPTY_OBJECT_ARRAY Object array
 1146               childState = stateList.toArray();
 1147           }
 1148   
 1149           stateStruct[CHILD_STATE] = childState;
 1150           return stateStruct;
 1151       }
 1152   
 1153       /**
 1154        * @throws NullPointerException {@inheritDoc}
 1155        */
 1156       public void processRestoreState(FacesContext context,
 1157                                       Object state) {
 1158           if (context == null) {
 1159               throw new NullPointerException();
 1160           }
 1161   
 1162           Object [] stateStruct = (Object []) state;
 1163           Object [] childState = (Object []) stateStruct[CHILD_STATE];
 1164   
 1165           // Process this component itself
 1166           restoreState(context, stateStruct[MY_STATE]);
 1167   
 1168           int i = 0;
 1169   
 1170           // Process all the children of this component
 1171           if (this.getChildCount() > 0) {
 1172               Iterator kids = getChildren().iterator();
 1173               while (kids.hasNext()) {
 1174                   UIComponent kid = (UIComponent) kids.next();
 1175                   if (kid.isTransient()) {
 1176                       continue;
 1177                   }
 1178                   Object currentState = childState[i++];
 1179                   if (currentState == null) {
 1180                       continue;
 1181                   }
 1182                   kid.processRestoreState(context, currentState);
 1183               }
 1184           }
 1185   
 1186           // process all of the facets of this component
 1187           if (this.getFacetCount() > 0) {
 1188               int facetsSize = getFacets().size();
 1189               int j = 0;
 1190               Object[] facetSaveState;
 1191               String facetName;
 1192               UIComponent facet;
 1193               Object facetState;
 1194               while (j < facetsSize) {
 1195                   if (null != (facetSaveState = (Object[])childState[i++])) {
 1196                       facetName = (String) facetSaveState[0];
 1197                       facetState = facetSaveState[1];
 1198                       facet = getFacets().get(facetName);
 1199                       facet.processRestoreState(context, facetState);
 1200                   }
 1201                   ++j;
 1202               }
 1203           }
 1204       }
 1205   
 1206       // ------------------------------------------------------- Protected Methods
 1207   
 1208   
 1209       protected FacesContext getFacesContext() {
 1210   
 1211   	// PENDING(edburns): we can't use the cache ivar because we
 1212   	// don't always know when to clear it.  For example, in the
 1213   	// "save state in server" case, the UIComponent instances stick
 1214   	// around between requests, yielding stale facesContext
 1215   	// references.  If there was some way to clear the facesContext
 1216   	// cache ivar for each node in the tree *after* the
 1217   	// render-response phase, then we could keep a cache ivar.  As
 1218   	// it is now, we must always use the Thread Local Storage
 1219   	// solution.
 1220   
 1221   	return FacesContext.getCurrentInstance();
 1222   
 1223       }
 1224   
 1225   
 1226       protected Renderer getRenderer(FacesContext context) {
 1227   
 1228           String rendererType = getRendererType();
 1229           Renderer result = null;
 1230           if (rendererType != null) {
 1231               result = context.getRenderKit().getRenderer(getFamily(),
 1232                                                           rendererType);
 1233               if (null == result) {
 1234                   if (log.isLoggable(Level.FINE)) {
 1235                       // PENDING(edburns): I18N
 1236                       log.fine("Can't get Renderer for type " + rendererType);
 1237                   }
 1238               }
 1239           } else {
 1240               if (log.isLoggable(Level.FINE)) {
 1241                   String id = this.getId();
 1242                   id = (null != id) ? id : this.getClass().getName();
 1243                   // PENDING(edburns): I18N
 1244                   log.fine("No renderer-type for component " + id);
 1245               }
 1246           }
 1247           return result;
 1248       }
 1249   
 1250   
 1251       // ----------------------------------------------------- StateHolder Methods
 1252       private Object[] values;
 1253   
 1254       public Object saveState(FacesContext context) {
 1255   
 1256           if (values == null) {
 1257                values = new Object[9];
 1258           }
 1259   
 1260           if (attributes != null) {
 1261               Map backing = attributes.getBackingAttributes();
 1262               if (backing != null && !backing.isEmpty()) {
 1263                   values[0] = backing;
 1264               }
 1265           }
 1266           values[1] = saveBindingsState(context);
 1267           values[2] = clientId;
 1268           values[3] = id;
 1269           values[4] = rendered ? Boolean.TRUE : Boolean.FALSE;
 1270           values[5] = renderedSet ? Boolean.TRUE : Boolean.FALSE;
 1271           values[6] = rendererType;
 1272           values[7] = saveAttachedState(context, listeners);
 1273           values[8] = attributesThatAreSet;
 1274           assert(!transientFlag);
 1275   
 1276           return (values);
 1277       }
 1278   
 1279   
 1280       public void restoreState(FacesContext context, Object state) {
 1281   
 1282           values = (Object[]) state;
 1283           // we need to get the map that knows how to handle attribute/property
 1284           // transparency before we restore its values.
 1285           if (values[0] != null) {
 1286               attributes = new AttributesMap(this,
 1287                                             (HashMap) TypedCollections.dynamicallyCastMap((Map) values[0],
 1288                                                                                           String.class,
 1289                                                                                           Object.class));
 1290           }
 1291           bindings = restoreBindingsState(context, values[1]);
 1292           clientId = (String) values[2];
 1293           id = (String) values[3];
 1294           rendered = ((Boolean) values[4]).booleanValue();
 1295           renderedSet = ((Boolean) values[5]).booleanValue();
 1296           rendererType = (String) values[6];
 1297           List<FacesListener> restoredListeners;
 1298           if (null != (restoredListeners = TypedCollections.dynamicallyCastList((List)
 1299                        restoreAttachedState(context, values[7]), FacesListener.class))) {
 1300               // if there were some listeners registered prior to this
 1301               // method being invoked, merge them with the list to be
 1302               // restored.
 1303               if (null != listeners) {
 1304   		listeners.addAll(restoredListeners);
 1305               }
 1306               else {
 1307                   listeners = restoredListeners;
 1308               }
 1309           }
 1310           attributesThatAreSet = (List<String>) values[8];
 1311       }
 1312   
 1313   
 1314       /**
 1315        * <p>Flag indicating a desire to now participate in state saving.</p>
 1316        */
 1317       private boolean transientFlag = false;
 1318   
 1319   
 1320       public boolean isTransient() {
 1321   
 1322           return (this.transientFlag);
 1323   
 1324       }
 1325   
 1326   
 1327       public void setTransient(boolean transientFlag) {
 1328   
 1329           this.transientFlag = transientFlag;
 1330   
 1331       }
 1332   
 1333       // -------------------------------------- Helper methods for state saving
 1334   
 1335       // --------- methods used by UIComponents to save their attached Objects.
 1336   
 1337       /**
 1338        *
 1339        * <p>This method is called by {@link UIComponent} subclasses that
 1340        * want to save one or more attached objects.  It is a convenience
 1341        * method that does the work of saving attached objects that may or
 1342        * may not implement the {@link StateHolder} interface.  Using this
 1343        * method implies the use of {@link #restoreAttachedState} to restore
 1344        * the attached objects.</p>
 1345        *
 1346        * <p>This method supports saving  attached objects of the following
 1347        * type: <code>Object</code>s,
 1348        * <code>null</code> values, and <code>Lists</code> of these
 1349        * objects.  If any contained objects are not <code>Lists</code>
 1350        * and do not implement {@link StateHolder}, they must have
 1351        * zero-argument public constructors.  The exact structure of the
 1352        * returned object is undefined and opaque, but will be serializable.
 1353        * </p>
 1354        *
 1355        * @param context the {@link FacesContext} for this request.
 1356        *
 1357        * @param attachedObject the object, which may be a
 1358        * <code>List</code> instance, or an Object.  The
 1359        * <code>attachedObject</code> (or the elements that comprise
 1360        * <code>attachedObject</code> may implement {@link StateHolder}.
 1361        *
 1362        * @throws NullPointerException if the context argument is null.
 1363        *
 1364        */
 1365   
 1366       public static Object saveAttachedState(FacesContext context,
 1367                                              Object attachedObject) {
 1368           if (null == context) {
 1369               throw new NullPointerException();
 1370           }
 1371           if (null == attachedObject) {
 1372               return null;
 1373           }
 1374           Object result;
 1375   
 1376           if (attachedObject instanceof List) {
 1377               List attachedList = (List) attachedObject;
 1378               List<StateHolderSaver> resultList = new ArrayList<StateHolderSaver>(attachedList.size());
 1379               Iterator listIter = attachedList.iterator();
 1380   	    Object cur;
 1381               while (listIter.hasNext()) {
 1382   		if (null != (cur = listIter.next())) {
 1383   		    resultList.add(new StateHolderSaver(context, cur));
 1384   		}
 1385               }
 1386               result = resultList;
 1387           }
 1388           else {
 1389               result = new StateHolderSaver(context, attachedObject);
 1390           }
 1391   
 1392           return result;
 1393       }
 1394   
 1395       /**
 1396        *
 1397        * <p>This method is called by {@link UIComponent} subclasses that
 1398        * need to restore the objects they saved using {@link
 1399        * #saveAttachedState}.  This method is tightly coupled with {@link
 1400        * #saveAttachedState}.</p>
 1401        *
 1402        * <p>This method supports restoring all attached objects types
 1403        * supported by {@link #saveAttachedState}.</p>
 1404        *
 1405        * @param context the {@link FacesContext} for this request
 1406        *
 1407        * @param stateObj the opaque object returned from {@link
 1408        * #saveAttachedState}
 1409        *
 1410        * @throws NullPointerException if context is null.
 1411        *
 1412        * @throws IllegalStateException if the object is not
 1413        *   previously returned by {@link #saveAttachedState}.
 1414        *
 1415        */
 1416   
 1417       public static Object restoreAttachedState(FacesContext context,
 1418                                                 Object stateObj)
 1419       throws IllegalStateException {
 1420           if (null == context) {
 1421               throw new NullPointerException();
 1422           }
 1423           if (null == stateObj) {
 1424               return null;
 1425           }
 1426           Object result;
 1427   
 1428           if (stateObj instanceof List) {
 1429               List stateList = (List) stateObj;
 1430               List<Object> retList = new ArrayList<Object>(stateList.size());
 1431               for (Object item : stateList) {
 1432                   try {
 1433                       retList.add(((StateHolderSaver) item).restore(context));
 1434                   } catch (ClassCastException cce) {
 1435                       throw new IllegalStateException("Unknown object type");
 1436                   }
 1437               }
 1438               result = retList;
 1439           } else if (stateObj instanceof StateHolderSaver) {
 1440               StateHolderSaver saver = (StateHolderSaver) stateObj;
 1441               result = saver.restore(context);
 1442           } else {
 1443               throw new IllegalStateException("Unknown object type");
 1444           }
 1445           return result;
 1446       }
 1447   
 1448       private static Map<String,ValueExpression> restoreBindingsState(FacesContext context, Object state) {
 1449   
 1450   	if (state == null) {
 1451   	    return (null);
 1452   	}
 1453   	Object values[] = (Object[]) state;
 1454   	String names[] = (String[]) values[0];
 1455   	Object states[] = (Object[]) values[1];
 1456   	Map<String,ValueExpression> bindings = new HashMap<String,ValueExpression>(names.length);
 1457   	for (int i = 0; i < names.length; i++) {
 1458   	    bindings.put(names[i],
 1459   			 (ValueExpression) restoreAttachedState(context, states[i]));
 1460   	}
 1461   	return (bindings);
 1462   
 1463       }
 1464   
 1465   
 1466       private Object saveBindingsState(FacesContext context) {
 1467   
 1468           if (bindings == null) {
 1469               return (null);
 1470           }
 1471   
 1472           Object values[] = new Object[2];
 1473           values[0] = bindings.keySet().toArray(new String[bindings.size()]);
 1474   
 1475           Object[] bindingValues = bindings.values().toArray();
 1476           for (int i = 0; i < bindingValues.length; i++) {
 1477               bindingValues[i] = saveAttachedState(context, bindingValues[i]);
 1478           }
 1479   
 1480           values[1] = bindingValues;
 1481   
 1482           return (values);
 1483   
 1484       }
 1485   
 1486   
 1487       Map<String,PropertyDescriptor> getDescriptorMap() {
 1488           return pdMap;
 1489       }
 1490   
 1491   
 1492       // --------------------------------------------------------- Private Classes
 1493   
 1494       // For state saving
 1495       private final static Object[] EMPTY_ARRAY = new Object[0];
 1496   
 1497       // Empty iterator for short circuiting operations
 1498       private final static Iterator<UIComponent> EMPTY_ITERATOR = new Iterator<UIComponent>() {
 1499   
 1500           public void remove() {
 1501               throw new UnsupportedOperationException();
 1502           }
 1503   
 1504           public UIComponent next() {
 1505               throw new NoSuchElementException("Empty Iterator");
 1506           }
 1507   
 1508           public boolean hasNext() {
 1509               return false;
 1510           }
 1511       };
 1512   
 1513       // Private implementation of Map that supports the functionality
 1514       // required by UIComponent.getFacets()
 1515       // HISTORY:
 1516       //   Versions 1.333 and older used inheritence to provide the
 1517       //     basic map functionality.  This was wasteful since a
 1518       //     component could be completely configured via ValueExpressions
 1519       //     or (Bindings) which means an EMPTY_OBJECT_ARRAY Map would always be
 1520       //     present when it wasn't needed.  By using composition,
 1521       //     we control if and when the Map is instantiated thereby
 1522       //     reducing uneeded object allocation.  This change also
 1523       //     has a nice side effect in state saving since we no
 1524       //     longer need to duplicate the map, we just provide the
 1525       //     private 'attributes' map directly to the state saving process.
 1526       private static class AttributesMap implements Map<String, Object>, Serializable {
 1527   
 1528           // this KEY is special to the AttributesMap - this allows the implementation
 1529           // to access the the List containing the attributes that have been set
 1530           private static final String ATTRIBUTES_THAT_ARE_SET_KEY =
 1531                 UIComponentBase.class.getName() + ".attributesThatAreSet";
 1532   
 1533           private HashMap<String, Object> attributes;
 1534           private transient Map<String,PropertyDescriptor> pdMap;
 1535           private transient UIComponent component;
 1536           private static final long serialVersionUID = -6773035086539772945L;
 1537   
 1538           // -------------------------------------------------------- Constructors
 1539   
 1540           private AttributesMap(UIComponent component) {
 1541   
 1542               this.component = component;
 1543               this.pdMap = ((UIComponentBase) component).getDescriptorMap();
 1544   
 1545           }
 1546   
 1547           private AttributesMap(UIComponent component,
 1548                                 HashMap<String,Object> attributes) {
 1549               this(component);
 1550               this.attributes = attributes;
 1551           }
 1552   
 1553           public boolean containsKey(Object keyObj) {
 1554               if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyObj)) {
 1555                   return true;
 1556               }
 1557               String key = (String) keyObj;
 1558               PropertyDescriptor pd =
 1559                   getPropertyDescriptor(key);
 1560               if (pd == null) {
 1561                   if (attributes != null) {
 1562                       return attributes.containsKey(key);
 1563                   } else {
 1564                       return (false);
 1565                   }
 1566               } else {
 1567                   return (false);
 1568               }
 1569           }
 1570   
 1571           public Object get(Object keyObj) {
 1572               String key = (String) keyObj;
 1573               if (key == null) {
 1574                   throw new NullPointerException();
 1575               }
 1576               if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
 1577                   return component.getAttributesThatAreSet(false);
 1578               }
 1579               PropertyDescriptor pd =
 1580                   getPropertyDescriptor(key);
 1581               if (pd != null) {
 1582                   try {
 1583                       Method readMethod = pd.getReadMethod();
 1584                       if (readMethod != null) {
 1585                           return (readMethod.invoke
 1586                                   (component, EMPTY_OBJECT_ARRAY));
 1587                       } else {
 1588                           throw new IllegalArgumentException(key);
 1589                       }
 1590                   } catch (IllegalAccessException e) {
 1591                       throw new FacesException(e);
 1592                   } catch (InvocationTargetException e) {
 1593                       throw new FacesException
 1594                           (e.getTargetException());
 1595                   }
 1596               } else if (attributes != null) {
 1597                   if (attributes.containsKey(key)) {
 1598                       return (attributes.get(key));
 1599                   }
 1600               }
 1601               ValueExpression ve = component.getValueExpression(key);
 1602               if (ve != null) {
 1603                   try {
 1604                       return ve.getValue(component.getFacesContext().getELContext());
 1605                   }
 1606                   catch (ELException e) {
 1607                       throw new FacesException(e);
 1608                   }
 1609               }
 1610               return (null);
 1611           }
 1612   
 1613           public Object put(String keyValue, Object value) {
 1614               if (keyValue == null) {
 1615                   throw new NullPointerException();
 1616               }
 1617   
 1618               if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyValue)) {
 1619                   if (component.attributesThatAreSet == null) {
 1620                       if (value instanceof List) {
 1621                           //noinspection unchecked
 1622                           component.attributesThatAreSet = (List<String>) value;
 1623                           return component.attributesThatAreSet;
 1624                       }
 1625                   }
 1626                   return null;
 1627               }
 1628   
 1629               PropertyDescriptor pd =
 1630                   getPropertyDescriptor(keyValue);
 1631               if (pd != null) {
 1632                   try {
 1633                       Object result = null;
 1634                       Method readMethod = pd.getReadMethod();
 1635                       if (readMethod != null) {
 1636                           result = readMethod.invoke
 1637                               (component, EMPTY_OBJECT_ARRAY);
 1638                       }
 1639                       Method writeMethod = pd.getWriteMethod();
 1640                       if (writeMethod != null) {
 1641                           writeMethod.invoke
 1642                               (component, value);
 1643                       } else {
 1644                           // TODO: i18n
 1645                           throw new IllegalArgumentException("Setter not found for property " + keyValue);
 1646                       }
 1647                       return (result);
 1648                   } catch (IllegalAccessException e) {
 1649                       throw new FacesException(e);
 1650                   } catch (InvocationTargetException e) {
 1651                       throw new FacesException
 1652                           (e.getTargetException());
 1653                   }
 1654               } else {
 1655                   if (value == null) {
 1656                       throw new NullPointerException();
 1657                   }
 1658                   if (attributes == null) {
 1659                       initMap();
 1660                   }
 1661                   List<String> sProperties = component.getAttributesThatAreSet(true);
 1662                   if (sProperties != null && !sProperties.contains(keyValue)) {
 1663                       sProperties.add(keyValue);
 1664                   }
 1665                   return (attributes.put(keyValue, value));
 1666               }
 1667           }
 1668   
 1669           public void putAll(Map<? extends String, ?> map) {
 1670               if (map == null) {
 1671                   throw new NullPointerException();
 1672               }
 1673   
 1674               for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
 1675                   this.put(entry.getKey(), entry.getValue());
 1676               }
 1677           }
 1678   
 1679           public Object remove(Object keyObj) {
 1680               String key = (String) keyObj;
 1681               if (key == null) {
 1682                   throw new NullPointerException();
 1683               }
 1684               if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
 1685                   return null;
 1686               }
 1687               PropertyDescriptor pd =
 1688                   getPropertyDescriptor(key);
 1689               if (pd != null) {
 1690                   throw new IllegalArgumentException(key);
 1691               } else {
 1692                   if (attributes != null) {
 1693                       List<String> sProperties = component.getAttributesThatAreSet(false);
 1694                       if (sProperties != null) {
 1695                           sProperties.remove(key);
 1696                       }
 1697                       return (attributes.remove(key));
 1698                   } else {
 1699                       return null;
 1700                   }
 1701               }
 1702           }
 1703   
 1704   
 1705           public int size() {
 1706               return (attributes != null ? attributes.size() : 0);
 1707           }
 1708   
 1709           public boolean isEmpty() {
 1710               return (attributes == null || attributes.isEmpty());
 1711           }
 1712   
 1713           public boolean containsValue(java.lang.Object value) {
 1714               return (attributes != null && attributes.containsValue(value));
 1715           }
 1716   
 1717           public void clear() {
 1718               if (attributes != null) {
 1719                   attributes.clear();
 1720               }
 1721           }
 1722   
 1723           public Set<String> keySet() {
 1724               if (attributes != null)
 1725           	return attributes.keySet();
 1726               return Collections.emptySet();
 1727           }
 1728   
 1729           public Collection<Object> values() {
 1730               if (attributes != null)
 1731           	return attributes.values();
 1732               return Collections.emptyList();
 1733           }
 1734   
 1735           public Set<Entry<String,Object>> entrySet() {
 1736               if (attributes != null)
 1737           	return attributes.entrySet();
 1738               return Collections.emptySet();
 1739           }
 1740   
 1741           Map getBackingAttributes() {
 1742               return attributes;
 1743           }
 1744   
 1745           public boolean equals(Object o) {
 1746               if (o == this) {
 1747                   return true;
 1748               }
 1749   
 1750               if (!(o instanceof Map)) {
 1751                   return false;
 1752               }
 1753               Map t = (Map) o;
 1754               if (t.size() != size()) {
 1755                   return false;
 1756               }
 1757   
 1758               try {
 1759                   for (Object e : entrySet()) {
 1760                       Entry entry = (Entry) e;
 1761                       Object key = entry.getKey();
 1762                       Object value = entry.getValue();
 1763                       if (value == null) {
 1764                           if (!(t.get(key) == null && t.containsKey(key))) {
 1765                               return false;
 1766                           }
 1767                       } else {
 1768                           if (!value.equals(t.get(key))) {
 1769                               return false;
 1770                           }
 1771                       }
 1772                   }
 1773               } catch (ClassCastException unused) {
 1774                   return false;
 1775               } catch (NullPointerException unused) {
 1776                   return false;
 1777               }
 1778   
 1779               return true;
 1780           }
 1781   
 1782   
 1783           public int hashCode() {
 1784               int h = 0;
 1785               for (Object o : entrySet()) {
 1786                   h += o.hashCode();
 1787               }
 1788               return h;
 1789           }
 1790   
 1791           private void initMap() {
 1792               attributes = new HashMap<String,Object>(8);
 1793           }
 1794   
 1795           /**
 1796            * <p>Return the <code>PropertyDescriptor</code> for the specified
 1797            * property name for this {@link UIComponent}'s implementation class,
 1798            * if any; otherwise, return <code>null</code>.</p>
 1799            *
 1800            * @param name Name of the property to return a descriptor for
 1801            * @throws FacesException if an introspection exception occurs
 1802            */
 1803           PropertyDescriptor getPropertyDescriptor(String name) {
 1804               if (pdMap != null) {
 1805                   return (pdMap.get(name));
 1806               }
 1807               return (null);
 1808            }
 1809   
 1810           // ----------------------------------------------- Serialization Methods
 1811   
 1812           // This is dependent on serialization occuring with in a
 1813           // a Faces request, however, since UIComponentBase.{save,restore}State()
 1814           // don't actually serialize the AttributesMap, these methods are here
 1815           // purely to be good citizens.
 1816           private void writeObject(ObjectOutputStream out) throws IOException {
 1817               if (attributes == null) {
 1818                   out.writeObject(new HashMap(1, 1.0f));
 1819               } else {
 1820                   out.writeObject(attributes);
 1821               }
 1822               out.writeObject(component.getClass());
 1823                //noinspection NonSerializableObjectPassedToObjectStream
 1824                out.writeObject(component.saveState(FacesContext.getCurrentInstance()));
 1825            }
 1826   
 1827           private void readObject(ObjectInputStream in)
 1828                throws IOException, ClassNotFoundException {
 1829               attributes = null;
 1830               pdMap = null;
 1831               component = null;
 1832               attributes = (HashMap) in.readObject();
 1833               Class clazz = (Class) in.readObject();
 1834               try {
 1835                   component = (UIComponent) clazz.newInstance();
 1836               } catch (Exception e) {
 1837                   throw new RuntimeException(e);
 1838               }
 1839               component.restoreState(FacesContext.getCurrentInstance(), in.readObject());
 1840           }
 1841       }
 1842   
 1843   
 1844       // Private implementation of List that supports the functionality
 1845       // required by UIComponent.getChildren()
 1846       private static class ChildrenList extends ArrayList<UIComponent> {
 1847   
 1848           private UIComponent component;
 1849   
 1850           public ChildrenList(UIComponent component) {
 1851               super(6);
 1852               this.component = component;
 1853           }
 1854   
 1855           public void add(int index, UIComponent element) {
 1856               if (element == null) {
 1857                   throw new NullPointerException();
 1858               } else if ((index < 0) || (index > size())) {
 1859                   throw new IndexOutOfBoundsException();
 1860               } else {
 1861                   eraseParent(element);
 1862                   element.setParent(component);
 1863                   super.add(index, element);
 1864               }
 1865           }
 1866   
 1867           public boolean add(UIComponent element) {
 1868               if (element == null) {
 1869                   throw new NullPointerException();
 1870               } else {
 1871                   eraseParent(element);
 1872                   element.setParent(component);
 1873                   return (super.add(element));
 1874               }
 1875           }
 1876   
 1877           public boolean addAll(Collection<? extends UIComponent> collection) {
 1878               Iterator<UIComponent> elements =
 1879                   (new ArrayList<UIComponent>(collection)).iterator();
 1880               boolean changed = false;
 1881               while (elements.hasNext()) {
 1882                   UIComponent element = elements.next();
 1883                   if (element == null) {
 1884                       throw new NullPointerException();
 1885                   } else {
 1886                       add(element);
 1887                       changed = true;
 1888                   }
 1889               }
 1890               return (changed);
 1891           }
 1892   
 1893           public boolean addAll(int index, Collection<? extends UIComponent> collection) {
 1894               Iterator<UIComponent> elements =
 1895                (new ArrayList<UIComponent>(collection)).iterator();
 1896               boolean changed = false;
 1897               while (elements.hasNext()) {
 1898                   UIComponent element = elements.next();
 1899                   if (element == null) {
 1900                       throw new NullPointerException();
 1901                   } else {
 1902                       add(index++, element);
 1903                       changed = true;
 1904                   }
 1905               }
 1906               return (changed);
 1907           }
 1908   
 1909           public void clear() {
 1910               int n = size();
 1911               if (n < 1) {
 1912                   return;
 1913               }
 1914               for (int i = 0; i < n; i++) {
 1915                   UIComponent child = get(i);
 1916                   child.setParent(null);
 1917               }
 1918               super.clear();
 1919           }
 1920   
 1921           public Iterator<UIComponent> iterator() {
 1922               return (new ChildrenListIterator(this));
 1923           }
 1924   
 1925           public ListIterator<UIComponent> listIterator() {
 1926               return (new ChildrenListIterator(this));
 1927           }
 1928   
 1929           public ListIterator<UIComponent> listIterator(int index) {
 1930               return (new ChildrenListIterator(this, index));
 1931           }
 1932   
 1933           public UIComponent remove(int index) {
 1934               UIComponent child = get(index);
 1935               super.remove(index);
 1936               child.setParent(null);
 1937               return (child);
 1938           }
 1939   
 1940           public boolean remove(Object elementObj) {
 1941               UIComponent element = (UIComponent) elementObj;
 1942               if (element == null) {
 1943                   throw new NullPointerException();
 1944               }
 1945   
 1946               if (super.remove(element)) {
 1947                   element.setParent(null);
 1948                   return (true);
 1949               } else {
 1950                   return (false);
 1951               }
 1952           }
 1953   
 1954           public boolean removeAll(Collection<?> collection) {
 1955               boolean result = false;
 1956               Iterator<?> elements = collection.iterator();
 1957               while (elements.hasNext()) {
 1958                   if (remove(elements.next())) {
 1959                       result = true;
 1960                   }
 1961               }
 1962               return (result);
 1963           }
 1964   
 1965           public boolean retainAll(Collection<?> collection) {
 1966               boolean modified = false;
 1967               Iterator<?> items = iterator();
 1968               while (items.hasNext()) {
 1969                   if (!collection.contains(items.next())) {
 1970                       items.remove();
 1971                       modified = true;
 1972                   }
 1973               }
 1974               return (modified);
 1975           }
 1976   
 1977           public UIComponent set(int index, UIComponent element) {
 1978               if (element == null) {
 1979                   throw new NullPointerException();
 1980               } else if ((index < 0) || (index >= size())) {
 1981                   throw new IndexOutOfBoundsException();
 1982               } else {
 1983                   eraseParent(element);
 1984                   UIComponent previous = get(index);
 1985                   previous.setParent(null);
 1986                   element.setParent(component);
 1987                   super.set(index, element);
 1988                   return (previous);
 1989               }
 1990           }
 1991   
 1992       }
 1993   
 1994   
 1995       // Private implementation of ListIterator for ChildrenList
 1996       private static class ChildrenListIterator implements ListIterator<UIComponent> {
 1997   
 1998   
 1999           public ChildrenListIterator(ChildrenList list) {
 2000               this.list = list;
 2001               this.index = 0;
 2002           }
 2003   
 2004           public ChildrenListIterator(ChildrenList list, int index) {
 2005               this.list = list;
 2006               if ((index < 0) || (index >= list.size())) {
 2007                   throw new IndexOutOfBoundsException(String.valueOf(index));
 2008               } else {
 2009                   this.index = index;
 2010               }
 2011           }
 2012   
 2013   
 2014           private ChildrenList list;
 2015           private int index;
 2016           private int last = -1; // Index last returned by next() or previous()
 2017   
 2018           // Iterator methods
 2019   
 2020           public boolean hasNext() {
 2021               return (index < list.size());
 2022           }
 2023   
 2024           public UIComponent next() {
 2025               try {
 2026                   UIComponent o = list.get(index);
 2027                   last = index++;
 2028                   return (o);
 2029               } catch (IndexOutOfBoundsException e) {
 2030                   throw new NoSuchElementException(String.valueOf(index));
 2031               }
 2032           }
 2033   
 2034           public void remove() {
 2035               if (last == -1) {
 2036                   throw new IllegalStateException();
 2037               }
 2038               list.remove(last);
 2039               if (last < index) {
 2040                   index--;
 2041               }
 2042               last = -1;
 2043           }
 2044   
 2045           // ListIterator methods
 2046   
 2047           public void add(UIComponent o) {
 2048               last = -1;
 2049               list.add(index++, o);
 2050           }
 2051   
 2052           public boolean hasPrevious() {
 2053               return (index > 1);
 2054           }
 2055   
 2056           public int nextIndex() {
 2057               return (index);
 2058           }
 2059   
 2060           public UIComponent previous() {
 2061               try {
 2062                   int current = index - 1;
 2063                   UIComponent o = list.get(current);
 2064                   last = current;
 2065                   index = current;
 2066                   return (o);
 2067               } catch (IndexOutOfBoundsException e) {
 2068                   throw new NoSuchElementException();
 2069               }
 2070           }
 2071   
 2072           public int previousIndex() {
 2073               return (index - 1);
 2074           }
 2075   
 2076           public void set(UIComponent o) {
 2077               if (last == -1) {
 2078                   throw new IllegalStateException();
 2079               }
 2080               list.set(last, o);
 2081           }
 2082   
 2083       }
 2084   
 2085   
 2086       // Private implementation of Iterator for getFacetsAndChildren()
 2087       private final static class FacetsAndChildrenIterator implements Iterator<UIComponent> {
 2088   
 2089           private Iterator<UIComponent> iterator;
 2090           private boolean childMode;
 2091           private UIComponent c;
 2092   
 2093           public FacetsAndChildrenIterator(UIComponent c) {
 2094               this.c = c;
 2095               this.childMode = false;
 2096           }
 2097   
 2098           private void update() {
 2099               if (this.iterator == null) {
 2100                   // we must guarantee that 'iterator' is never null
 2101                   if (this.c.getFacetCount() != 0) {
 2102                       this.iterator = this.c.getFacets().values().iterator();
 2103                       this.childMode = false;
 2104                   } else if (this.c.getChildCount() != 0) {
 2105                       this.iterator = this.c.getChildren().iterator();
 2106                       this.childMode = true;
 2107                   } else {
 2108                       this.iterator = EMPTY_ITERATOR;
 2109                       this.childMode = true;
 2110                   }
 2111               } else if (!this.childMode
 2112                       && !this.iterator.hasNext()
 2113                       && this.c.getChildCount() != 0) {
 2114                   this.iterator = this.c.getChildren().iterator();
 2115                   this.childMode = true;
 2116               }
 2117           }
 2118   
 2119           public boolean hasNext() {
 2120               this.update();
 2121               return this.iterator.hasNext();
 2122           }
 2123   
 2124           public UIComponent next() {
 2125               this.update();
 2126               return this.iterator.next();
 2127           }
 2128   
 2129           public void remove() {
 2130               throw new UnsupportedOperationException();
 2131           }
 2132   
 2133       }
 2134   
 2135   
 2136       // Private implementation of Map that supports the functionality
 2137       // required by UIComponent.getFacets()
 2138       private static class FacetsMap extends HashMap<String, UIComponent> {
 2139   
 2140           UIComponent component;
 2141   
 2142           public FacetsMap(UIComponent component) {
 2143               super(3, 1.0f);
 2144               this.component = component;
 2145           }
 2146   
 2147           public void clear() {
 2148               Iterator<String> keys = keySet().iterator();
 2149               while (keys.hasNext()) {
 2150                   keys.next();
 2151                   keys.remove();
 2152               }
 2153               super.clear();
 2154           }
 2155   
 2156           public Set<Map.Entry<String, UIComponent>> entrySet() {
 2157               return (new FacetsMapEntrySet(this));
 2158           }
 2159   
 2160           public Set<String> keySet() {
 2161               return (new FacetsMapKeySet(this));
 2162           }
 2163   
 2164           public UIComponent put(String key, UIComponent value) {
 2165               if ((key == null) || (value == null)) {
 2166                   throw new NullPointerException();
 2167               } else //noinspection ConstantConditions
 2168                   if (!(key instanceof String) ||
 2169                          !(value instanceof UIComponent)) {
 2170                   throw new ClassCastException();
 2171               }
 2172               UIComponent previous = super.get(key);
 2173               if (previous != null) {
 2174                   previous.setParent(null);
 2175               }
 2176               eraseParent(value);
 2177               value.setParent(component);
 2178               return (super.put(key, value));
 2179           }
 2180   
 2181           public void putAll(Map<? extends String, ? extends UIComponent> map) {
 2182               if (map == null) {
 2183                   throw new NullPointerException();
 2184               }
 2185               for (Map.Entry<? extends String, ? extends UIComponent> entry : map.entrySet()) {
 2186                   put(entry.getKey(), entry.getValue());
 2187               }
 2188           }
 2189   
 2190           public UIComponent remove(Object key) {
 2191               UIComponent previous = get(key);
 2192               if (previous != null) {
 2193                   previous.setParent(null);
 2194               }
 2195               super.remove(key);
 2196               return (previous);
 2197           }
 2198   
 2199           public Collection<UIComponent> values() {
 2200               return (new FacetsMapValues(this));
 2201           }
 2202   
 2203           Iterator<String> keySetIterator() {
 2204               return ((new ArrayList<String>(super.keySet())).iterator());
 2205           }
 2206   
 2207       }
 2208   
 2209   
 2210       // Private implementation of Set for FacetsMap.getEntrySet()
 2211       private static class FacetsMapEntrySet extends AbstractSet<Map.Entry<String, UIComponent>> {
 2212   
 2213           public FacetsMapEntrySet(FacetsMap map) {
 2214               this.map = map;
 2215           }
 2216   
 2217           private FacetsMap map = null;
 2218   
 2219           public boolean add(Map.Entry<String, UIComponent> o) {
 2220               throw new UnsupportedOperationException();
 2221           }
 2222   
 2223           public boolean addAll(Collection<? extends Map.Entry<String,UIComponent>> c) {
 2224               throw new UnsupportedOperationException();
 2225           }
 2226   
 2227           public void clear() {
 2228               map.clear();
 2229           }
 2230   
 2231           public boolean contains(Object o) {
 2232               if (o == null) {
 2233                   throw new NullPointerException();
 2234               }
 2235               if (!(o instanceof Map.Entry)) {
 2236                   return (false);
 2237               }
 2238               Map.Entry e = (Map.Entry) o;
 2239               Object k = e.getKey();
 2240               Object v = e.getValue();
 2241               if (!map.containsKey(k)) {
 2242                   return (false);
 2243               }
 2244               if (v == null) {
 2245                   return (map.get(k) == null);
 2246               } else {
 2247                   return (v.equals(map.get(k)));
 2248               }
 2249           }
 2250   
 2251           public boolean isEmpty() {
 2252               return (map.isEmpty());
 2253           }
 2254   
 2255           public Iterator<Map.Entry<String, UIComponent>> iterator() {
 2256               return (new FacetsMapEntrySetIterator(map));
 2257           }
 2258   
 2259           public boolean remove(Object o) {
 2260               if (o == null) {
 2261                   throw new NullPointerException();
 2262               }
 2263               if (!(o instanceof Map.Entry)) {
 2264                   return (false);
 2265               }
 2266               Object k = ((Map.Entry) o).getKey();
 2267               if (map.containsKey(k)) {
 2268                   map.remove(k);
 2269                   return (true);
 2270               } else {
 2271                   return (false);
 2272               }
 2273           }
 2274   
 2275           public boolean removeAll(Collection c) {
 2276               boolean result = false;
 2277               Iterator v = c.iterator();
 2278               while (v.hasNext()) {
 2279                   if (remove(v.next())) {
 2280                       result = true;
 2281                   }
 2282               }
 2283               return (result);
 2284           }
 2285   
 2286           public boolean retainAll(Collection c) {
 2287               boolean result = false;
 2288               Iterator v = iterator();
 2289               while (v.hasNext()) {
 2290                   if (!c.contains(v.next())) {
 2291                       v.remove();
 2292                       result = true;
 2293                   }
 2294               }
 2295               return (result);
 2296           }
 2297   
 2298           public int size() {
 2299               return (map.size());
 2300           }
 2301   
 2302       }
 2303   
 2304   
 2305       // Private implementation of Map.Entry for FacetsMapEntrySet
 2306       private static class FacetsMapEntrySetEntry implements Map.Entry<String, UIComponent> {
 2307   
 2308           public FacetsMapEntrySetEntry(FacetsMap map, String key) {
 2309               this.map = map;
 2310               this.key = key;
 2311           }
 2312   
 2313           private FacetsMap map;
 2314           private String key;
 2315   
 2316           public boolean equals(Object o) {
 2317               if (o == null) {
 2318                   return (false);
 2319               }
 2320               if (!(o instanceof Map.Entry)) {
 2321                   return (false);
 2322               }
 2323               Map.Entry e = (Map.Entry) o;
 2324               if (key == null) {
 2325                   if (e.getKey() != null) {
 2326                       return (false);
 2327                   }
 2328               } else {
 2329                   if (!key.equals(e.getKey())) {
 2330                       return (false);
 2331                   }
 2332               }
 2333               UIComponent v = map.get(key);
 2334               if (v == null) {
 2335                   if (e.getValue() != null) {
 2336                       return (false);
 2337                   }
 2338               } else {
 2339                   if (!v.equals(e.getValue())) {
 2340                       return (false);
 2341                   }
 2342               }
 2343               return (true);
 2344           }
 2345   
 2346           public String getKey() {
 2347               return (key);
 2348           }
 2349   
 2350           public UIComponent getValue() {
 2351               return (map.get(key));
 2352           }
 2353   
 2354           public int hashCode() {
 2355               Object value = map.get(key);
 2356               return (((key == null) ? 0 : key.hashCode()) ^
 2357                       ((value == null) ? 0 : value.hashCode()));
 2358           }
 2359   
 2360           public UIComponent setValue(UIComponent value) {
 2361               UIComponent previous = map.get(key);
 2362               map.put(key, value);
 2363               return (previous);
 2364           }
 2365   
 2366       }
 2367   
 2368   
 2369       // Private implementation of Set for FacetsMap.getEntrySet().iterator()
 2370       private static class FacetsMapEntrySetIterator implements Iterator<Map.Entry<String, UIComponent>> {
 2371   
 2372           public FacetsMapEntrySetIterator(FacetsMap map) {
 2373               this.map = map;
 2374               this.iterator = map.keySetIterator();
 2375           }
 2376   
 2377           private FacetsMap map = null;
 2378           private Iterator<String> iterator = null;
 2379           private Map.Entry<String, UIComponent> last = null;
 2380   
 2381           public boolean hasNext() {
 2382               return (iterator.hasNext());
 2383           }
 2384   
 2385           public Map.Entry<String, UIComponent> next() {
 2386               last = new FacetsMapEntrySetEntry(map, iterator.next());
 2387               return (last);
 2388           }
 2389   
 2390           public void remove() {
 2391               if (last == null) {
 2392                   throw new IllegalStateException();
 2393               }
 2394               map.remove(((Map.Entry) last).getKey());
 2395               last = null;
 2396           }
 2397   
 2398       }
 2399   
 2400   
 2401       // Private implementation of Set for FacetsMap.getKeySet()
 2402       private static class FacetsMapKeySet extends AbstractSet<String> {
 2403   
 2404           public FacetsMapKeySet(FacetsMap map) {
 2405               this.map = map;
 2406           }
 2407   
 2408           private FacetsMap map = null;
 2409   
 2410           public boolean add(String o) {
 2411               throw new UnsupportedOperationException();
 2412           }
 2413   
 2414           public boolean addAll(Collection<? extends String> c) {
 2415               throw new UnsupportedOperationException();
 2416           }
 2417   
 2418           public void clear() {
 2419               map.clear();
 2420           }
 2421   
 2422           public boolean contains(Object o) {
 2423               return (map.containsKey(o));
 2424           }
 2425   
 2426           public boolean containsAll(Collection c) {
 2427               Iterator v = c.iterator();
 2428               while (v.hasNext()) {
 2429                   if (!map.containsKey(v.next())) {
 2430                       return (false);
 2431                   }
 2432               }
 2433               return (true);
 2434           }
 2435   
 2436           public boolean isEmpty() {
 2437               return (map.isEmpty());
 2438           }
 2439   
 2440           public Iterator<String> iterator() {
 2441               return (new FacetsMapKeySetIterator(map));
 2442           }
 2443   
 2444           public boolean remove(Object o) {
 2445               if (map.containsKey(o)) {
 2446                   map.remove(o);
 2447                   return (true);
 2448               } else {
 2449                   return (false);
 2450               }
 2451           }
 2452   
 2453           public boolean removeAll(Collection c) {
 2454               boolean result = false;
 2455               Iterator v = c.iterator();
 2456               while (v.hasNext()) {
 2457                   Object o = v.next();
 2458                   if (map.containsKey(o)) {
 2459                       map.remove(o);
 2460                       result = true;
 2461                   }
 2462               }
 2463               return (result);
 2464           }
 2465   
 2466           public boolean retainAll(Collection c) {
 2467               boolean result = false;
 2468               Iterator v = iterator();
 2469               while (v.hasNext()) {
 2470                   if (!c.contains(v.next())) {
 2471                       v.remove();
 2472                       result = true;
 2473                   }
 2474               }
 2475               return (result);
 2476           }
 2477   
 2478           public int size() {
 2479               return (map.size());
 2480           }
 2481   
 2482       }
 2483   
 2484   
 2485       // Private implementation of Set for FacetsMap.getKeySet().iterator()
 2486       private static class FacetsMapKeySetIterator implements Iterator<String> {
 2487   
 2488           public FacetsMapKeySetIterator(FacetsMap map) {
 2489               this.map = map;
 2490               this.iterator = map.keySetIterator();
 2491           }
 2492   
 2493           private FacetsMap map = null;
 2494           private Iterator<String> iterator = null;
 2495           private String last = null;
 2496   
 2497           public boolean hasNext() {
 2498               return (iterator.hasNext());
 2499           }
 2500   
 2501           public String next() {
 2502               last = iterator.next();
 2503               return (last);
 2504           }
 2505   
 2506           public void remove() {
 2507               if (last == null) {
 2508                   throw new IllegalStateException();
 2509               }
 2510               map.remove(last);
 2511               last = null;
 2512           }
 2513   
 2514       }
 2515   
 2516   
 2517       // Private implementation of Collection for FacetsMap.values()
 2518       private static class FacetsMapValues extends AbstractCollection<UIComponent> {
 2519   
 2520           public FacetsMapValues(FacetsMap map) {
 2521               this.map = map;
 2522           }
 2523   
 2524           private FacetsMap map;
 2525   
 2526           public boolean add(UIComponent o) {
 2527               throw new UnsupportedOperationException();
 2528           }
 2529   
 2530           public boolean addAll(Collection c) {
 2531               throw new UnsupportedOperationException();
 2532           }
 2533   
 2534           public void clear() {
 2535               map.clear();
 2536           }
 2537   
 2538           public boolean isEmpty() {
 2539               return (map.isEmpty());
 2540           }
 2541   
 2542           public Iterator<UIComponent> iterator() {
 2543               return (new FacetsMapValuesIterator(map));
 2544           }
 2545   
 2546           public int size() {
 2547               return (map.size());
 2548           }
 2549   
 2550   
 2551       }
 2552   
 2553   
 2554       // Private implementation of Iterator for FacetsMap.values().iterator()
 2555       private static class FacetsMapValuesIterator implements Iterator<UIComponent> {
 2556   
 2557           public FacetsMapValuesIterator(FacetsMap map) {
 2558               this.map = map;
 2559               this.iterator = map.keySetIterator();
 2560           }
 2561   
 2562           private FacetsMap map = null;
 2563           private Iterator<String> iterator = null;
 2564           private Object last = null;
 2565   
 2566           public boolean hasNext() {
 2567               return (iterator.hasNext());
 2568           }
 2569   
 2570           public UIComponent next() {
 2571               last = iterator.next();
 2572               return (map.get(last));
 2573           }
 2574   
 2575           public void remove() {
 2576               if (last == null) {
 2577                   throw new IllegalStateException();
 2578               }
 2579               map.remove(last);
 2580               last = null;
 2581           }
 2582   
 2583       }
 2584   
 2585   
 2586   
 2587   
 2588   }

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.component » [javadoc | source]