/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.faces.component;

import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.component.behavior.Behavior;
import javax.faces.component.behavior.ClientBehavior;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.*;
import javax.faces.render.Renderer;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <p><strong class="changed_modified_2_0 changed_modified_2_0_rev_a changed_added_2_1">UIComponentBase</strong> is a
 * convenience base class that implements the default concrete behavior
 * of all methods defined by {@link UIComponent}.</p>
 * <p/>
 * <p>By default, this class defines <code>getRendersChildren()</code>
 * to find the renderer for this component and call its
 * <code>getRendersChildren()</code> method.  The default implementation
 * on the <code>Renderer</code> returns <code>false</code>.  As of
 * version 1.2 of the JavaServer Faces Specification, component authors
 * are encouraged to return <code>true</code> from this method and rely
 * on the implementation of {@link #encodeChildren} in this class and in
 * the Renderer ({@link Renderer#encodeChildren}).  Subclasses that wish
 * to manage the rendering of their children should override this method
 * to return <code>true</code> instead.</p>
 */
public abstract class UIComponentBase extends UIComponent {


    // -------------------------------------------------------------- Attributes

    private static Logger LOGGER = Logger.getLogger("javax.faces.component",
            "javax.faces.LogStrings");

    private static final String ADDED = UIComponentBase.class.getName() + ".ADDED";


    /**
     * <p>Each entry is an map of <code>PropertyDescriptor</code>s describing
     * the properties of a concrete {@link UIComponent} implementation, keyed
     * by the corresponding <code>java.lang.Class</code>.</p>
     * <p/>
     * <p><strong>IMPLEMENTATION NOTE</strong> - This is implemented as a
     * <code>WeakHashMap</code> so that, even if this class is embedded in a
     * container's class loader that is a parent to webapp class loaders,
     * references to the classes will eventually expire.</p>
     */
    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
    private static Map<Class<?>, Map<String, PropertyDescriptor>>
            descriptors =
            new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>();

    /**
     * Reference to the map of <code>PropertyDescriptor</code>s for this class
     * in the <code>descriptors<code> <code>Map<code>.
     */
    private Map<String, PropertyDescriptor> pdMap = null;

    private Map<Class<? extends SystemEvent>, List<SystemEventListener>> listenersByEventClass;

    /**
     * <p>An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods.</p>
     */
    private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0];

    public UIComponentBase() {
        populateDescriptorsMapIfNecessary();
    }

    private void populateDescriptorsMapIfNecessary() {
        Class<?> clazz = this.getClass();
        synchronized(descriptors) {
            pdMap = descriptors.get(clazz);
        }
        if (null != pdMap) {
            return;
        }

        // load the property descriptors for this class.
        PropertyDescriptor pd[] = getPropertyDescriptors();
        if (pd != null) {
            pdMap = new HashMap<String, PropertyDescriptor>(pd.length, 1.0f);
            for (PropertyDescriptor aPd : pd) {
                pdMap.put(aPd.getName(), aPd);
            }
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.log(Level.FINE, "fine.component.populating_descriptor_map",
                        new Object[]{clazz,
                                Thread.currentThread().getName()});
            }

            // Check again
            Map<String, PropertyDescriptor> reCheckMap = null;
            synchronized(descriptors) {
                reCheckMap = descriptors.get(clazz);
            }
            if (null != reCheckMap) {
                return;
            }
            synchronized(descriptors) {
                descriptors.put(clazz, pdMap);
            }
        }


    }


    /**
     * <p>Return an array of <code>PropertyDescriptors</code> for this
     * {@link UIComponent}'s implementation class.  If no descriptors
     * can be identified, a zero-length array will be returned.</p>
     *
     * @throws FacesException if an introspection exception occurs
     */
    private PropertyDescriptor[] getPropertyDescriptors() {
        PropertyDescriptor[] pd;
        try {
            pd = Introspector.getBeanInfo(this.getClass()).
                    getPropertyDescriptors();
        } catch (IntrospectionException e) {
            throw new FacesException(e);
        }
        return (pd);
    }


    /**
     * <p>The <code>Map</code> containing our attributes, keyed by
     * attribute name.</p>
     */
    private AttributesMap attributes = null;


    public Map<String, Object> getAttributes() {

        if (attributes == null) {
            attributes = new AttributesMap(this);
        }
        return (attributes);

    }

    @Override
    public Map<String, Object> getPassThroughAttributes(boolean create) {
        Map<String, Object> result = (Map<String, Object>) 
                this.getStateHelper().get(PropertyKeys.passThroughAttributes);
        if (null == result) {
            if (create) {
                result = new PassThroughAttributesMap<String, Object>();
                this.getStateHelper().put(PropertyKeys.passThroughAttributes, 
                        result);
            }
        }
        
        return result;

    }

    private static class PassThroughAttributesMap<K, V> extends ConcurrentHashMap<String, Object> implements Serializable {
        @Override
        public Object put(String key, Object value) {
            if (null == key || null == value) {
                throw new NullPointerException();
            }
            validateKey(key);
            return super.put(key, value);
        }
        
        @Override
        public Object putIfAbsent(String key, Object value) {
            if (null == key || null == value) {
                throw new NullPointerException();
            }
            validateKey(key);
            return super.putIfAbsent(key, value);
        }
        
        private void validateKey(Object key) {
            if (!(key instanceof String) || (key instanceof ValueExpression) || !(key instanceof Serializable)) {
                throw new IllegalArgumentException();
            }
        }
        
    }
    
    // ---------------------------------------------------------------- Bindings


    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException {@inheritDoc}
     * @deprecated This has been replaced by {@link #getValueExpression}.
     */
    public ValueBinding getValueBinding(String name) {

        if (name == null) {
            throw new NullPointerException();
        }
        ValueBinding result = null;
        ValueExpression ve;

        if (null != (ve = getValueExpression(name))) {
            // if the ValueExpression is an instance of our private
            // wrapper class.
            if (ve.getClass().equals(ValueExpressionValueBindingAdapter.class)) {
                result = ((ValueExpressionValueBindingAdapter) ve).getWrapped();
            } else {
                // otherwise, this is a real ValueExpression.  Wrap it
                // in a ValueBinding.
                result = new ValueBindingValueExpressionAdapter(ve);
            }
        }
        return result;
    }


    /**
     * {@inheritDoc}
     *
     * @throws IllegalArgumentException {@inheritDoc}
     * @throws NullPointerException     {@inheritDoc}
     * @deprecated This has been replaced by {@link #setValueExpression}.
     */
    public void setValueBinding(String name, ValueBinding binding) {
        if (name == null) {
            throw new NullPointerException();
        }
        if (binding != null) {
            ValueExpressionValueBindingAdapter adapter =
                    new ValueExpressionValueBindingAdapter(binding);
            setValueExpression(name, adapter);
        } else {
            setValueExpression(name, null);
        }

    }


    // -------------------------------------------------------------- Properties


    /**
     * <p>The assigned client identifier for this component.</p>
     */
    private String clientId = null;


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public String getClientId(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // if the clientId is not yet set
        if (this.clientId == null) {
            UIComponent namingContainerAncestor =
                    this.getNamingContainerAncestor();
            UIComponent parent = namingContainerAncestor;
            String parentId = null;

            // give the parent the opportunity to first
            // grab a unique clientId
            if (parent != null) {
                parentId = parent.getContainerClientId(context);
            }

            // now resolve our own client id
            this.clientId = getId();
            if (this.clientId == null) {
                String generatedId;
                if (null != namingContainerAncestor &&
                    namingContainerAncestor instanceof UniqueIdVendor) {
                    generatedId = ((UniqueIdVendor)namingContainerAncestor).createUniqueId(context, null);
                }
                else {
                    generatedId = context.getViewRoot().createUniqueId();
                }
                setId(generatedId);
                this.clientId = getId();
            }
            if (parentId != null) {
                StringBuilder idBuilder =
                        new StringBuilder(parentId.length()
                                + 1
                                + this.clientId.length());
                this.clientId = idBuilder.append(parentId)
                        .append(UINamingContainer.getSeparatorChar(context))
                        .append(this.clientId).toString();
            }

            // allow the renderer to convert the clientId
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                this.clientId = renderer.convertClientId(context, this.clientId);
            }
        }
        return this.clientId;
    }

    /**
     * <p>The component identifier for this component.</p>
     */
    private String id = null;


    public String getId() {

        return (id);

    }

    private UIComponent getNamingContainerAncestor() {
	UIComponent namingContainer = this.getParent();
        while (namingContainer != null) {
            if (namingContainer instanceof NamingContainer) {
                return namingContainer;
            }
            namingContainer = namingContainer.getParent();
        }
        return null;
    }


    /**
     * @throws IllegalArgumentException {@inheritDoc}
     * @throws IllegalStateException    {@inheritDoc}
     */
    public void setId(String id) {

        // if the current ID is not null, and the passed
        // argument is the same, no need to validate it
        // as it has already been validated.
        if (this.id == null || !(this.id.equals(id))) {
            validateId(id);
            this.id = id;
        }

        this.clientId = null; // Erase any cached value

    }


    /**
     * <p>The parent component for this component.</p>
     */
    private UIComponent parent = null;


    public UIComponent getParent() {
        return (this.parent);
    }


    public void setParent(UIComponent parent) {


        if (parent == null) {
            if (this.parent != null) {
                doPreRemoveProcessing(FacesContext.getCurrentInstance(), this);
                this.parent = parent;
            }
            compositeParent = null;
        } else {
            this.parent = parent;
            if (this.getAttributes().get(ADDED) == null) {
                // add an attribute to this component here to indiciate that
                // it's being processed.  If we don't do this, and the component
                // is re-parented, the events could fire again in certain cases
                // and cause a stack overflow.
                this.getAttributes().put(ADDED, Boolean.TRUE);
                doPostAddProcessing(FacesContext.getCurrentInstance(), this);
                // remove the attribute once we've returned from the event
                // processing.
                this.getAttributes().remove(ADDED);
            }
        }

    }



    public boolean isRendered() {
        
	return Boolean.valueOf(getStateHelper().eval(PropertyKeys.rendered, Boolean.TRUE).toString());        
    }


    public void setRendered(boolean rendered) {
        getStateHelper().put(PropertyKeys.rendered, rendered);
    }


    public String getRendererType() {

        return (String) getStateHelper().eval(PropertyKeys.rendererType);
        
    }


    public void setRendererType(String rendererType) {

        getStateHelper().put(PropertyKeys.rendererType, rendererType);

    }


    public boolean getRendersChildren() {
        boolean result = false;

        Renderer renderer;
        if (getRendererType() != null) {
            if (null !=
                    (renderer = getRenderer(getFacesContext()))) {
                result = renderer.getRendersChildren();
            }
        }
        return result;

    }

    // ------------------------------------------------- Tree Management Methods


    /*
     * <p>The <code>List</code> containing our child components.</p>
     */
    private List<UIComponent> children = null;


    public List<UIComponent> getChildren() {

        if (children == null) {
            children = new ChildrenList(this);
        }
        return (children);

    }


    // Do not allocate the children List to answer this question
    public int getChildCount() {

        if (children != null) {
            return (children.size());
        } else {
            return (0);
        }

    }


    /**
     * <p>If the specified {@link UIComponent} has a non-null parent,
     * remove it as a child or facet (as appropriate) of that parent.
     * As a result, the <code>parent</code> property will always be
     * <code>null</code> when this method returns.</p>
     *
     * @param component {@link UIComponent} to have any parent erased
     */
    private static void eraseParent(UIComponent component) {

        UIComponent parent = component.getParent();
        if (parent == null) {
            return;
        }
        if (parent.getChildCount() > 0) {
            List children = parent.getChildren();
            int index = children.indexOf(component);
            if (index >= 0) {
                children.remove(index);
                return;
            }
        }
        if (parent.getFacetCount() > 0) {
            Map facets = parent.getFacets();
            Iterator entries = facets.entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry entry = (Map.Entry) entries.next();
                //noinspection ObjectEquality
                if (entry.getValue() == component) {
                    entries.remove();
                    return;
                }
            }
        }

        // Throw an exception for the "cannot happen" case
        throw new IllegalStateException("Parent was not null, " +
                "but this component not related");

    }

    /**
     * <p>Throw <code>IllegalArgumentException</code> if the specified
     * component identifier is non-<code>null</code> and not
     * syntactically valid.  </p>
     *
     * @param id The component identifier to test
     */
    private static void validateId(String id) {

        if (id == null) {
            return;
        }
        int n = id.length();
        if (n < 1) {
            throw new IllegalArgumentException("Empty id attribute is not allowed");
        }
        for (int i = 0; i < n; i++) {
            char c = id.charAt(i);
            if (i == 0) {
                if (!Character.isLetter(c) && (c != '_')) {
                    throw new IllegalArgumentException(id);
                }
            } else {
                if (!Character.isLetter(c) &&
                        !Character.isDigit(c) &&
                        (c != '-') && (c != '_')) {
                    throw new IllegalArgumentException(id);
                }
            }
        }

    }


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public UIComponent findComponent(String expr) {
        if (expr == null) {
            throw new NullPointerException();
        }

        FacesContext ctx = FacesContext.getCurrentInstance();
        final char sepChar = UINamingContainer.getSeparatorChar(ctx);
        final String SEPARATOR_STRING = String.valueOf(sepChar);

        if (expr.length() == 0) {
            // if an empty value is provided, fail fast.
            throw new IllegalArgumentException("\"\"");
        }

        // Identify the base component from which we will perform our search
        UIComponent base = this;
        if (expr.charAt(0) == sepChar) {
            // Absolute searches start at the root of the tree
            while (base.getParent() != null) {
                base = base.getParent();
            }
            // Treat remainder of the expression as relative
            expr = expr.substring(1);
        } else if (!(base instanceof NamingContainer)) {
            // Relative expressions start at the closest NamingContainer or root
            while (base.getParent() != null) {
                if (base instanceof NamingContainer) {
                    break;
                }
                base = base.getParent();
            }
        }

        // Evaluate the search expression (now guaranteed to be relative)
        UIComponent result = null;
        String[] segments = expr.split(SEPARATOR_STRING);
        for (int i = 0, length = (segments.length - 1);
             i < segments.length;
             i++, length--) {
            result = findComponent(base, segments[i], (i == 0));
            // the first element of the expression may match base.id
            // (vs. a child if of base)
            if (i == 0 && result == null &&
                    segments[i].equals(base.getId())) {
                result = base;
            }
            if (result != null && (!(result instanceof NamingContainer)) && length > 0) {
                throw new IllegalArgumentException(segments[i]);
            }
            if (result == null) {
                break;
            }
            base = result;
        }

        // Return the final result of our search
        return (result);

    }


    /**
     * <p>Return the {@link UIComponent} (if any) with the specified
     * <code>id</code>, searching recursively starting at the specified
     * <code>base</code>, and examining the base component itself, followed
     * by examining all the base component's facets and children (unless
     * the base component is a {@link NamingContainer}, in which case the
     * recursive scan is skipped.</p>
     *
     * @param base Base {@link UIComponent} from which to search
     * @param id   Component identifier to be matched
     */
    private static UIComponent findComponent(UIComponent base,
                                             String id,
                                             boolean checkId) {
        if (checkId && id.equals(base.getId())) {
            return base;
        }
        // Search through our facets and children
        UIComponent result = null;
        for (Iterator i = base.getFacetsAndChildren(); i.hasNext();) {
            UIComponent kid = (UIComponent) i.next();
            if (!(kid instanceof NamingContainer)) {
                if (checkId && id.equals(kid.getId())) {
                    result = kid;
                    break;
                }
                result = findComponent(kid, id, true);
                if (result != null) {
                    break;
                }
            } else if (id.equals(kid.getId())) {
                result = kid;
                break;
            }
        }
        return (result);

    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException {@inheritDoc}
     * @throws FacesException       {@inheritDoc}
     * @since 1.2
     */
    public boolean invokeOnComponent(FacesContext context, String clientId,
                                     ContextCallback callback)
            throws FacesException {
        return super.invokeOnComponent(context, clientId, callback);
    }


    // ------------------------------------------------ Facet Management Methods


    /*
     * <p>The <code>Map</code> containing our related facet components.</p>
     */
    private Map<String, UIComponent> facets = null;


    public Map<String, UIComponent> getFacets() {

        if (facets == null) {
            facets = new FacetsMap(this);
        }
        return (facets);

    }

    // Do not allocate the children List to answer this question
    public int getFacetCount() {

        if (facets != null) {
            return (facets.size());
        } else {
            return (0);
        }

    }


    // Do not allocate the facets Map to answer this question
    public UIComponent getFacet(String name) {

        if (facets != null) {
            return (facets.get(name));
        } else {
            return (null);
        }

    }


    public Iterator<UIComponent> getFacetsAndChildren() {

        Iterator<UIComponent> result;
        int childCount = this.getChildCount(),
                facetCount = this.getFacetCount();
        // If there are neither facets nor children
        if (0 == childCount && 0 == facetCount) {
            result = EMPTY_ITERATOR;
        }
        // If there are only facets and no children
        else if (0 == childCount) {
            Collection<UIComponent> unmodifiable =
                    Collections.unmodifiableCollection(getFacets().values());
            result = unmodifiable.iterator();
        }
        // If there are only children and no facets
        else if (0 == facetCount) {
            List<UIComponent> unmodifiable =
                    Collections.unmodifiableList(getChildren());
            result = unmodifiable.iterator();
        }
        // If there are both children and facets
        else {
            result = new FacetsAndChildrenIterator(this);
        }
        return result;
    }


    // -------------------------------------------- Lifecycle Processing Methods

    /**
     * @throws AbortProcessingException {@inheritDoc}
     * @throws IllegalStateException    {@inheritDoc}
     * @throws NullPointerException     {@inheritDoc}
     */
    public void broadcast(FacesEvent event)
        throws AbortProcessingException {

        if (event == null) {
            throw new NullPointerException();
        }
        if (event instanceof BehaviorEvent) {
            BehaviorEvent behaviorEvent = (BehaviorEvent) event;
            Behavior behavior = behaviorEvent.getBehavior();
            behavior.broadcast(behaviorEvent);
        }

        if (listeners == null) {
            return;
        }

        for (FacesListener listener : listeners.asArray(FacesListener.class)) {
            if (event.isAppropriateListener(listener)) {
                event.processListener(listener);
            }
        }
    }


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void decode(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }
        String rendererType = getRendererType();
        if (rendererType != null) {
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                renderer.decode(context, this);
            } else {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.fine("Can't get Renderer for type " + rendererType);
                }
            }
        }
    }


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void encodeBegin(FacesContext context) throws IOException {

        if (context == null) {
            throw new NullPointerException();
        }

        pushComponentToEL(context, null);

        if (!isRendered()) {
            return;
        }

        context.getApplication().publishEvent(context,
                                              PreRenderComponentEvent.class,
                                              this);

        String rendererType = getRendererType();
        if (rendererType != null) {
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                renderer.encodeBegin(context, this);
            } else {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.fine("Can't get Renderer for type " + rendererType);
                }
            }
        }

    }

    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void encodeChildren(FacesContext context) throws IOException {

        if (context == null) {
            throw new NullPointerException();
        }
        if (!isRendered()) {
            return;
        }
        String rendererType = getRendererType();
        if (rendererType != null) {
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                renderer.encodeChildren(context, this);
            }
            // We've already logged for this component
        } else {
            if (getChildCount() > 0) {
                for (UIComponent child : getChildren()) {
                    child.encodeAll(context);
                }
            }
        }
    }


    /**
     * @throws IOException          {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void encodeEnd(FacesContext context) throws IOException {

        if (context == null) {
            throw new NullPointerException();
        }
        if (!isRendered()) {
            popComponentFromEL(context);
            return;
        }
        String rendererType = getRendererType();
        if (rendererType != null) {
            Renderer renderer = this.getRenderer(context);
            if (renderer != null) {
                renderer.encodeEnd(context, this);
            }
        }
        popComponentFromEL(context);

    }

    // -------------------------------------------------- Event Listener Methods

    private AttachedObjectListHolder<FacesListener> listeners;

    /**
     * <p>Add the specified {@link FacesListener} to the set of listeners
     * registered to receive event notifications from this {@link UIComponent}.
     * It is expected that {@link UIComponent} classes acting as event sources
     * will have corresponding typesafe APIs for registering listeners of the
     * required type, and the implementation of those registration methods
     * will delegate to this method.  For example:</p>
     * <pre>
     * public class FooEvent extends FacesEvent {
     *   ...
     *   protected boolean isAppropriateListener(FacesListener listener) {
     *     return (listener instanceof FooListener);
     *   }
     *   protected void processListener(FacesListener listener) {
     *     ((FooListener) listener).processFoo(this);
     *   }
     *   ...
     * }
     * <p/>
     * public interface FooListener extends FacesListener {
     *   public void processFoo(FooEvent event);
     * }
     * <p/>
     * public class FooComponent extends UIComponentBase {
     *   ...
     *   public void addFooListener(FooListener listener) {
     *     addFacesListener(listener);
     *   }
     *   public void removeFooListener(FooListener listener) {
     *     removeFacesListener(listener);
     *   }
     *   ...
     * }
     * </pre>
     *
     * @param listener The {@link FacesListener} to be registered
     * @throws NullPointerException if <code>listener</code>
     *                              is <code>null</code>
     */
    protected void addFacesListener(FacesListener listener) {

        if (listener == null) {
            throw new NullPointerException();
        }

        if (listeners == null) {
            listeners = new AttachedObjectListHolder<FacesListener>();
        }

        listeners.add(listener);

    }


    /**
     * @throws IllegalArgumentException {@inheritDoc}
     * @throws NullPointerException     {@inheritDoc}
     */
    protected FacesListener[] getFacesListeners(Class clazz) {

        if (clazz == null) {
            throw new NullPointerException();
        }
        if (!FacesListener.class.isAssignableFrom(clazz)) {
            throw new IllegalArgumentException();
        }

        if (this.listeners == null) {
            return (FacesListener[]) Array.newInstance(clazz, 0);
        }
        FacesListener[] listeners = this.listeners.asArray(FacesListener.class);
        if (listeners.length == 0) {
            return (FacesListener[]) Array.newInstance(clazz, 0);
        }

        List<FacesListener> results = new ArrayList<FacesListener>(listeners.length);
        for (FacesListener listener : listeners) {
            if (((Class<?>) clazz).isAssignableFrom(listener.getClass())) {
                results.add(listener);
            }
        }

        return (results.toArray
                ((FacesListener[]) Array.newInstance(clazz,
                        results.size())));

    }


    /**
     * <p>Remove the specified {@link FacesListener} from the set of listeners
     * registered to receive event notifications from this {@link UIComponent}.
     *
     * @param listener The {@link FacesListener} to be deregistered
     * @throws NullPointerException if <code>listener</code>
     *                              is <code>null</code>
     */
    protected void removeFacesListener(FacesListener listener) {

        if (listener == null) {
            throw new NullPointerException();
        }

        if (listeners != null) {
            listeners.remove(listener);
        }

    }

    /**
     * @throws IllegalStateException {@inheritDoc}
     * @throws NullPointerException  {@inheritDoc}
     */
    public void queueEvent(FacesEvent event) {

        if (event == null) {
            throw new NullPointerException();
        }
        UIComponent parent = getParent();
        if (parent == null) {
            throw new IllegalStateException();
        } else {
            parent.queueEvent(event);
        }

    }

    /**
     * <p class="changed_added_2_1">Install the listener instance
     * referenced by argument <code>componentListener</code> as a
     * listener for events of type <code>eventClass</code> originating
     * from this specific instance of <code>UIComponent</code>.  The
     * default implementation creates an inner {@link
     * SystemEventListener} instance that wraps argument
     * <code>componentListener</code> as the <code>listener</code>
     * argument.  This inner class must call through to the argument
     * <code>componentListener</code> in its implementation of {@link
     * SystemEventListener#processEvent} and its implementation of
     * {@link SystemEventListener#isListenerForSource} must return
     * true if the instance class of this <code>UIComponent</code> is
     * assignable from the argument to
     * <code>isListenerForSource</code>.</p>
     *
     * @param eventClass the <code>Class</code> of event for which
     * <code>listener</code> must be fired.
     * @param componentListener the implementation of {@link
     * javax.faces.event.ComponentSystemEventListener} whose {@link
     * javax.faces.event.ComponentSystemEventListener#processEvent} method must be called
     * when events of type <code>facesEventClass</code> are fired.
     *
     * @throws <code>NullPointerException</code> if any of the
     * arguments are <code>null</code>.
     *
     * @since 2.1
     */
    public void subscribeToEvent(Class<? extends SystemEvent> eventClass,
                                 ComponentSystemEventListener componentListener) {

        if (eventClass == null) {
            throw new NullPointerException();
        }
        if (componentListener == null) {
            throw new NullPointerException();
        }

        if (initialStateMarked()) {
            initialState = false;
        }
        if (null == listenersByEventClass) {
            listenersByEventClass = new HashMap<Class<? extends SystemEvent>,
                                                List<SystemEventListener>>(3, 1.0f);
        }
        SystemEventListener facesLifecycleListener =
              new ComponentSystemEventListenerAdapter(componentListener, this);
        List<SystemEventListener> listenersForEventClass =
              listenersByEventClass.get(eventClass);
        if (listenersForEventClass == null) {
            listenersForEventClass = new ArrayList<SystemEventListener>(3);
            listenersByEventClass.put(eventClass, listenersForEventClass);
        }
        if (!listenersForEventClass.contains(facesLifecycleListener)) {
            listenersForEventClass.add(facesLifecycleListener);
        }

    }

    /**
     * <p class="changed_added_2_1">Remove the listener instance
     *     referenced by argument <code>componentListener</code> as a
     *     listener for events of type <code>eventClass</code>
     *     originating from this specific instance of
     *     <code>UIComponent</code>.  When doing the comparison to
     *     determine if an existing listener is equal to the argument
     *     <code>componentListener</code> (and thus must be removed),
     *     the <code>equals()</code> method on the <em>existing
     *     listener</em> must be invoked, passing the argument
     *     <code>componentListener</code>, rather than the other way
     *     around.</p>
     *
     * @param eventClass the <code>Class</code> of event for which
     * <code>listener</code> must be removed.
     * @param componentListener the implementation of {@link
     * ComponentSystemEventListener} whose {@link
     * ComponentSystemEventListener#processEvent} method must no longer be called
     * when events of type <code>eventClass</code> are fired.
     *
     * @throws <code>NullPointerException</code> if any of the
     * arguments are <code>null</code>.
     *
     * @since 2.1
     */
    public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass,
                                     ComponentSystemEventListener componentListener) {

        if (eventClass == null) {
            throw new NullPointerException();
        }
        if (componentListener == null) {
            throw new NullPointerException();
        }

        List<SystemEventListener> listeners =
              getListenersForEventClass(eventClass);
        if (listeners != null && !listeners.isEmpty()) {
            for (Iterator<SystemEventListener> i = listeners.iterator(); i.hasNext();) {
                SystemEventListener item = i.next();
                ComponentSystemEventListenerAdapter csla =
                      (ComponentSystemEventListenerAdapter) item;
                ComponentSystemEventListener l = csla.getWrapped();
                if (l.equals(componentListener)) {
                    i.remove();
                    break;
                }
            }
        }

    }

    /**
     * <p class="changed_added_2_1">Return the
     * <code>SystemEventListener</code> instances registered on this
     * <code>UIComponent</code> instance that are interested in events
     * of type <code>eventClass</code>.</p>
     *
     * @param eventClass the <code>Class</code> of event for which the
     * listeners must be returned.

     * @throws NullPointerException if argument <code>eventClass</code> is <code>null</code>.
     *
     * @since 2.1
     */
    public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass) {

        if (eventClass == null) {
            throw new NullPointerException();
        }
        List<SystemEventListener> result = null;
        if (listenersByEventClass != null) {
            result = listenersByEventClass.get(eventClass);
        }
        return result;

    }


    // ------------------------------------------------ Lifecycle Phase Handlers


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void processDecodes(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        pushComponentToEL(context, null);

        // Process all facets and children of this component
        Iterator kids = getFacetsAndChildren();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            kid.processDecodes(context);
        }

        // Process this component itself
        try {
            decode(context);
        } catch (RuntimeException e) {
            context.renderResponse();
            throw e;
        } finally {
            popComponentFromEL(context);
        }

    }


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void processValidators(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        pushComponentToEL(context, null);

        Application app = context.getApplication();
        app.publishEvent(context, PreValidateEvent.class, this);
        // Process all the facets and children of this component
        Iterator kids = getFacetsAndChildren();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            kid.processValidators(context);
        }
        app.publishEvent(context, PostValidateEvent.class, this);
        popComponentFromEL(context);

    }


    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void processUpdates(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        pushComponentToEL(context, null);

        // Process all facets and children of this component
        Iterator kids = getFacetsAndChildren();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            kid.processUpdates(context);

        }
        popComponentFromEL(context);
        
    }

    private static final int MY_STATE = 0;
    private static final int CHILD_STATE = 1;

    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public Object processSaveState(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }
        if (this.isTransient()) {
            return null;
        }
        Object[] stateStruct = new Object[2];
        Object[] childState = EMPTY_ARRAY;

        pushComponentToEL(context, null);

        // Process this component itself
        stateStruct[MY_STATE] = saveState(context);

        // determine if we have any children to store
        int count = this.getChildCount() + this.getFacetCount();
        if (count > 0) {

            // this arraylist will store state
            List<Object> stateList = new ArrayList<Object>(count);

            // if we have children, add them to the stateList
            if (this.getChildCount() > 0) {
                Iterator kids = getChildren().iterator();
                UIComponent kid;
                while (kids.hasNext()) {
                    kid = (UIComponent) kids.next();
                    if (!kid.isTransient()) {
                        stateList.add(kid.processSaveState(context));
                        popComponentFromEL(context);
                    }
                }
            }

            pushComponentToEL(context, null);

            // if we have facets, add them to the stateList
            if (this.getFacetCount() > 0) {
                Iterator myFacets = getFacets().entrySet().iterator();
                UIComponent facet;
                Object facetState;
                Object[] facetSaveState;
                Map.Entry entry;
                while (myFacets.hasNext()) {
                    entry = (Map.Entry) myFacets.next();
                    facet = (UIComponent) entry.getValue();
                    if (!facet.isTransient()) {
                        facetState = facet.processSaveState(context);
                        popComponentFromEL(context);
                        facetSaveState = new Object[2];
                        facetSaveState[0] = entry.getKey();
                        facetSaveState[1] = facetState;
                        stateList.add(facetSaveState);
                    }
                }
            }

            // finally, capture the stateList and replace the original,
            // EMPTY_OBJECT_ARRAY Object array
            childState = stateList.toArray();
        }

        stateStruct[CHILD_STATE] = childState;
        return stateStruct;
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void processRestoreState(FacesContext context,
                                    Object state) {
        if (context == null) {
            throw new NullPointerException();
        }

        Object[] stateStruct = (Object[]) state;
        Object[] childState = (Object[]) stateStruct[CHILD_STATE];

        // Process this component itself
        restoreState(context, stateStruct[MY_STATE]);

        int i = 0;

        // Process all the children of this component
        if (this.getChildCount() > 0) {
            for (UIComponent kid : getChildren()) {
                if (kid.isTransient()) {
                    continue;
                }
                Object currentState = childState[i++];
                if (currentState == null) {
                    continue;
                }
                pushComponentToEL(context, null);
                kid.processRestoreState(context, currentState);
                popComponentFromEL(context);
            }
        }

        // process all of the facets of this component
        if (this.getFacetCount() > 0) {
            int facetsSize = getFacets().size();
            int j = 0;
            Object[] facetSaveState;
            String facetName;
            UIComponent facet;
            Object facetState;
            while (j < facetsSize) {
                if (null != (facetSaveState = (Object[]) childState[i++])) {
                    facetName = (String) facetSaveState[0];
                    facetState = facetSaveState[1];
                    facet = getFacets().get(facetName);
                    pushComponentToEL(context, null);
                    facet.processRestoreState(context, facetState);
                    popComponentFromEL(context);
                }
                ++j;
            }
        }
    }

    // ------------------------------------------------------- Protected Methods

    protected FacesContext getFacesContext() {

        // PENDING(edburns): we can't use the cache ivar because we
        // don't always know when to clear it.  For example, in the
        // "save state in server" case, the UIComponent instances stick
        // around between requests, yielding stale facesContext
        // references.  If there was some way to clear the facesContext
        // cache ivar for each node in the tree *after* the
        // render-response phase, then we could keep a cache ivar.  As
        // it is now, we must always use the Thread Local Storage
        // solution.

        return FacesContext.getCurrentInstance();

    }


    protected Renderer getRenderer(FacesContext context) {

        String rendererType = getRendererType();
        Renderer result = null;
        if (rendererType != null) {
            result = context.getRenderKit().getRenderer(getFamily(),
                    rendererType);
            if (null == result) {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.fine("Can't get Renderer for type " + rendererType);
                }
            }
        } else {
            if (LOGGER.isLoggable(Level.FINE)) {
                String id = this.getId();
                id = (null != id) ? id : this.getClass().getName();
                LOGGER.fine("No renderer-type for component " + id);
            }
        }
        return result;
    }


    // ---------------------------------------------- PartialStateHolder Methods

    /**
     * <p class="changed_added_2_0">For each of the attached objects on
     * this instance that implement {@link PartialStateHolder}, call
     * {@link PartialStateHolder#markInitialState} on the attached object.</p> 
     * @since 2.0
     */
    @Override
    public void markInitialState() {
        super.markInitialState();

        if (listeners != null) {
            listeners.markInitialState();
        }
        if (listenersByEventClass != null) {
            for (List<SystemEventListener> listener : listenersByEventClass.values()) {
                if (listener instanceof PartialStateHolder) {
                    ((PartialStateHolder) listener).markInitialState();
                }
            }
        }
        if (behaviors != null) {
            for (Entry<String, List<ClientBehavior>> entry : behaviors.entrySet()) {
                for (ClientBehavior behavior : entry.getValue()) {
                    if (behavior instanceof PartialStateHolder) {
                        ((PartialStateHolder) behavior).markInitialState();
                    }
                }
            }
        }
    }


    /**
     * <p class="changed_added_2_0">For each of the attached objects on
     * this instance that implement {@link PartialStateHolder}, call
     * {@link PartialStateHolder#clearInitialState} on the attached object.</p> 
     * @since 2.0
     */
    @Override
    public void clearInitialState() {
        super.clearInitialState();
        if (listeners != null) {
            listeners.clearInitialState();
        }
        if (listenersByEventClass != null) {
            for (List<SystemEventListener> listener : listenersByEventClass.values()) {
                if (listener instanceof PartialStateHolder) {
                    ((PartialStateHolder) listener).clearInitialState();
                }
            }
        }
        if (behaviors != null) {
            for (Entry<String, List<ClientBehavior>> entry : behaviors.entrySet()) {
                for (ClientBehavior behavior : entry.getValue()) {
                    if (behavior instanceof PartialStateHolder) {
                        ((PartialStateHolder) behavior).clearInitialState();
                    }
                }
            }
        }
    }


    public Object saveState(FacesContext context) {
        Object[] values = null;
        if (context == null) {
            throw new NullPointerException();
        }
        assert (!transientFlag);
        if (initialStateMarked()) {
            Object savedFacesListeners = ((listeners != null) ? listeners.saveState(context) : null);
            Object savedSysEventListeners = saveSystemEventListeners(context);
            Object savedBehaviors = saveBehaviorsState(context);
            Object savedBindings = null;
            if (bindings != null) {
                savedBindings = saveBindingsState(context);
            }
            Object savedHelper = null;
            if (stateHelper != null) {
                savedHelper = stateHelper.saveState(context);
            }
            if (savedFacesListeners == null
                  && savedSysEventListeners == null
                  && savedBehaviors == null
                  && savedBindings == null
                  && savedHelper == null) {
                return null;
            } else {
                if (values == null || values.length != 5) {
                    values = new Object[5];
                }

                // since we're saving partial state, skip id and clientId
                // as this will be reconstructed from the template execution
                // when the view is restored
                values[0] = savedFacesListeners;
                values[1] = savedSysEventListeners;
                values[2] = savedBehaviors;
                values[3] = savedBindings;
                values[4] = savedHelper;
                return values;
            }
        } else {
            if (values == null || values.length != 6) {
                values = new Object[6];
            }

            values[0] = ((listeners != null) ? listeners.saveState(context) : null);
            values[1] = saveSystemEventListeners(context);
            values[2] = saveBehaviorsState(context);
            if (bindings != null) {
                values[3] = saveBindingsState(context);
            }
            if (stateHelper != null) {
                values[4] = stateHelper.saveState(context);
            }
            values[5] = id;
            
            return (values);
        }
    }


    public void restoreState(FacesContext context, Object state) {

        if (context == null) {
            throw new NullPointerException();
        }

        if (state == null) {
            return;
        }
        Object[] values = (Object[]) state;

        if (values[0] != null) {
            if (listeners == null) {
                listeners = new AttachedObjectListHolder<FacesListener>();
            }
            listeners.restoreState(context, values[0]);
        }
        if (values[1] != null) {
            Map m = restoreSystemEventListeners(context, values[1]);
            if (listenersByEventClass != null) {
                listenersByEventClass.putAll(m);
            } else {
                listenersByEventClass = m;
            }
        }
        if (values[2] != null) {
            behaviors = restoreBehaviorsState(context, values[2]);
        }
        if (values[3] != null) {
            bindings = restoreBindingsState(context, values[3]);
        }
        if(values[4] != null) {
            getStateHelper().restoreState(context, values[4]);
        }
        if (values.length == 6) {
            // this means we've saved full state and need to do a little more
            // work to finish the job
            if (values[5] != null) {
                id = (String) values[5];
            }
        }

    }


    /**
     * <p>Flag indicating a desire to now participate in state saving.</p>
     */
    private boolean transientFlag = false;

    public boolean isTransient() {

        return (this.transientFlag);

    }


    public void setTransient(boolean transientFlag) {

        this.transientFlag = transientFlag;

    }

    // -------------------------------------- Helper methods for state saving

    // --------- methods used by UIComponents to save their attached Objects.

    /**
     * <p class="changed_modified_2_0">This method is called by {@link
     * UIComponent} subclasses that want to save one or more attached
     * objects.  It is a convenience method that does the work of saving
     * attached objects that may or may not implement the {@link
     * StateHolder} interface.  Using this method implies the use of
     * {@link #restoreAttachedState} to restore the attached
     * objects.</p>
     * <p/>
     * <p>This method supports saving attached objects of the following
     * type: <code>Object</code>s, <code>null</code> values, and <code
     * class="changed_modified_2_0">Collection</code>s of these objects.
     * If any contained objects are not <code
     * class="changed_modified_2_0">Collection</code>s and do not
     * implement {@link StateHolder}, they must have zero-argument
     * public constructors.  The exact structure of the returned object
     * is undefined and opaque, but will be serializable.  </p>
     *
     * @param context        the {@link FacesContext} for this request.
     * @param attachedObject the object, which may be a
     *                       <code>List</code> instance, or an Object.  The
     *                       <code>attachedObject</code> (or the elements that comprise
     *                       <code>attachedObject</code> may implement {@link StateHolder}.
     * @throws NullPointerException if the context argument is null.
     */

    public static Object saveAttachedState(FacesContext context,
                                           Object attachedObject) {
        if (null == context) {
            throw new NullPointerException();
        }
        if (null == attachedObject) {
            return null;
        }
        Object result;
        Class mapOrCollectionClass = attachedObject.getClass();
        boolean newWillSucceed = true;
        // first, test for newability of the class.
        try {
            int modifiers = mapOrCollectionClass.getModifiers();
            newWillSucceed = Modifier.isPublic(modifiers);
            if (newWillSucceed) {
                newWillSucceed = null != mapOrCollectionClass.getConstructor();
            }
        } catch (Exception e) {
            newWillSucceed = false;
        }


        if (newWillSucceed && attachedObject instanceof Collection) {
            Collection attachedCollection = (Collection) attachedObject;
            List<StateHolderSaver> resultList = null;
            for (Object item : attachedCollection) {
                if (item != null) {
                    if (item instanceof StateHolder && ((StateHolder) item).isTransient()) {
                        continue;
                    }
                    if (resultList == null) {
                        resultList = new ArrayList<StateHolderSaver>(attachedCollection.size() + 1);
                        resultList.add(new StateHolderSaver(context, mapOrCollectionClass));
                    }
                    resultList.add(new StateHolderSaver(context, item));
                }
            }
            result = resultList;
        } else if (newWillSucceed && attachedObject instanceof Map) {
            Map<Object, Object> attachedMap = (Map<Object, Object>) attachedObject;
            List<StateHolderSaver> resultList = null;
            Object key, value;
            for (Map.Entry<Object, Object> entry : attachedMap.entrySet()) {
                key = entry.getKey();
                if (key instanceof StateHolder && ((StateHolder)key).isTransient()) {
                    continue;
                }
                value = entry.getValue();
                if (value instanceof StateHolder && ((StateHolder)value).isTransient()) {
                    continue;
                }
                if (resultList == null) {
                    resultList = new ArrayList<StateHolderSaver>(attachedMap.size()*2 + 1);
                    resultList.add(new StateHolderSaver(context, mapOrCollectionClass));
                }
                resultList.add(new StateHolderSaver(context, key));
                resultList.add(new StateHolderSaver(context, value));
            }
            result = resultList;
        } else {
            result = new StateHolderSaver(context, attachedObject);
        }

        return result;
    }

    /**
     * <p>This method is called by {@link UIComponent} subclasses that
     * need to restore the objects they saved using {@link
     * #saveAttachedState}.  This method is tightly coupled with {@link
     * #saveAttachedState}.</p>
     * <p/>
     * <p>This method supports restoring all attached objects types
     * supported by {@link #saveAttachedState}.</p>
     *
     * @param context  the {@link FacesContext} for this request
     * @param stateObj the opaque object returned from {@link
     *                 #saveAttachedState}
     * @throws NullPointerException  if context is null.
     * @throws IllegalStateException if the object is not
     *                               previously returned by {@link #saveAttachedState}.
     */

    public static Object restoreAttachedState(FacesContext context,
                                              Object stateObj)
            throws IllegalStateException {
        if (null == context) {
            throw new NullPointerException();
        }
        if (null == stateObj) {
            return null;
        }
        Object result;

        if (stateObj instanceof List) {
            List<StateHolderSaver> stateList = (List<StateHolderSaver>) stateObj;
            StateHolderSaver collectionSaver = stateList.get(0);
            Class mapOrCollection = (Class) collectionSaver.restore(context);
            if (Collection.class.isAssignableFrom(mapOrCollection)) {
                Collection<Object> retCollection = null;
                try {
                    retCollection = (Collection<Object>) mapOrCollection.newInstance();
                } catch (Exception e) {
                    if (LOGGER.isLoggable(Level.SEVERE)) {
                        LOGGER.log(Level.SEVERE, e.toString(), e);
                    }
                    throw new IllegalStateException("Unknown object type");
                }
                for (int i = 1, len = stateList.size(); i < len; i++) {
                    try {
                        retCollection.add(stateList.get(i).restore(context));
                    } catch (ClassCastException cce) {
                        if (LOGGER.isLoggable(Level.SEVERE)) {
                            LOGGER.log(Level.SEVERE, cce.toString(), cce);
                        }
                        throw new IllegalStateException("Unknown object type");
                    }
                }
                result = retCollection;
            } else {
                // If we were doing assertions: assert(mapOrList.isAssignableFrom(Map.class));
                Map<Object, Object> retMap = null;
                try {
                    retMap = (Map<Object,Object>) mapOrCollection.newInstance();
                } catch (Exception e) {
                    if (LOGGER.isLoggable(Level.SEVERE)) {
                        LOGGER.log(Level.SEVERE, e.toString(), e);
                    }
                    throw new IllegalStateException("Unknown object type");
                }
                for (int i = 1, len = stateList.size(); i < len; i+=2) {
                    try {
                        retMap.put(stateList.get(i).restore(context),
                                   stateList.get(i+1).restore(context));
                    } catch (ClassCastException cce) {
                        if (LOGGER.isLoggable(Level.SEVERE)) {
                            LOGGER.log(Level.SEVERE, cce.toString(), cce);
                        }
                        throw new IllegalStateException("Unknown object type");
                    }
                }
                result = retMap;

            }
        } else if (stateObj instanceof StateHolderSaver) {
            StateHolderSaver saver = (StateHolderSaver) stateObj;
            result = saver.restore(context);
        } else {
            throw new IllegalStateException("Unknown object type");
        }
        return result;
    }

    private static Map<String, ValueExpression> restoreBindingsState(FacesContext context, Object state) {

        if (state == null) {
            return (null);
        }
        Object values[] = (Object[]) state;
        String names[] = (String[]) values[0];
        Object states[] = (Object[]) values[1];
        Map<String, ValueExpression> bindings = new HashMap<String, ValueExpression>(names.length);
        for (int i = 0; i < names.length; i++) {
            bindings.put(names[i],
                    (ValueExpression) restoreAttachedState(context, states[i]));
        }
        return (bindings);

    }


    private Object saveBindingsState(FacesContext context) {

        if (bindings == null) {
            return (null);
        }

        Object values[] = new Object[2];
        values[0] = bindings.keySet().toArray(new String[bindings.size()]);

        Object[] bindingValues = bindings.values().toArray();
        for (int i = 0; i < bindingValues.length; i++) {
            bindingValues[i] = saveAttachedState(context, bindingValues[i]);
        }

        values[1] = bindingValues;

        return (values);

    }

    private Object saveSystemEventListeners(FacesContext ctx) {

        if (listenersByEventClass == null) {
            return null;
        }

        int size = listenersByEventClass.size();
        Object listeners[][] = new Object[size][2];
        int idx = 0;
        boolean savedState = false;
        for (Entry<Class<? extends SystemEvent>, List<SystemEventListener>> e : listenersByEventClass.entrySet()) {
            Object[] target = listeners[idx++];
            target[0] = e.getKey();
            target[1] = saveAttachedState(ctx, e.getValue());
            if (target[1] == null) {
                target[0] = null;
            } else {
                savedState = true;
            }
        }

        return ((savedState) ? listeners : null);

    }


    private Map<Class<? extends SystemEvent>, List<SystemEventListener>> restoreSystemEventListeners(FacesContext ctx, Object state) {

        if (state == null) {
            return null;
        }

        Object[][] listeners = (Object[][]) state;
        Map<Class<? extends SystemEvent>, List<SystemEventListener>> m =
                new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>(listeners.length, 1.0f);
        for (int i = 0, len = listeners.length; i < len; i++) {
            Object[] source = listeners[i];
            m.put((Class<? extends SystemEvent>) source[0],
                    (List<SystemEventListener>) restoreAttachedState(ctx, source[1]));
        }

        return m;
    }


    Map<String, PropertyDescriptor> getDescriptorMap() {
        return pdMap;
    }


    private void doPostAddProcessing(FacesContext context, UIComponent added) {

        if (parent.isInView()) {
            publishAfterViewEvents(context, context.getApplication(), added);
        }

    }

    private void doPreRemoveProcessing(FacesContext context,
                                       UIComponent toRemove) {

        if (parent.isInView()) {
            disconnectFromView(context, context.getApplication(), toRemove);
        }

    }

    //------------------------------------------------------------- BehaviorHolder stub methods.

    /**
     * behaviors associated with this component.
     */
    private BehaviorsMap behaviors;

    /**
     * <p class="changed_added_2_0">This is a default implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior}.
     * <code>UIComponent</code> does not implement the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
     * but provides default implementations for the methods defined by
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
     * subclass implementations.   Subclasses that wish to support the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract must
     * declare that the subclass implements
     * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must provide
     * an implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p>
     *
     * @param eventName the logical name of the client-side event to attach
     *        the behavior to.
     * @param  behavior the {@link javax.faces.component.behavior.Behavior} 
     * instance to attach for the specified event name.
     *
     * @since 2.0
     */
    public void addClientBehavior(String eventName, ClientBehavior behavior) {
        assertClientBehaviorHolder();
        // First, make sure that the event is supported.  We don't want
        // to bother attaching behaviors for unsupported events.

        Collection<String> eventNames = getEventNames();

        // getClientEventNames() is spec'ed to require a non-null Set.
        // If getClientEventNames() returns null, throw an exception
        // to indicate that the API in not being used properly.
        if (eventNames == null) {
            throw new IllegalStateException(
                "Attempting to add a Behavior to a component " +
                "that does not support any event types. "     +
                  "getEventTypes() must return a non-null Set.");
        }

        if (eventNames.contains(eventName)) {

            if (initialStateMarked()) {
                // a Behavior has been added dynamically.  Update existing
                // Behaviors, if any, to save their full state.
                if (behaviors != null) {
                    for (Entry<String, List<ClientBehavior>> entry : behaviors
                          .entrySet()) {
                        for (ClientBehavior b : entry.getValue()) {
                            if (b instanceof PartialStateHolder) {
                                ((PartialStateHolder) behavior).clearInitialState();
                            }
                        }
                    }
                }
            }
            // We've got an event that we support, create our Map
            // if necessary

            if (null == behaviors) {
                // Typically we only have a small number of behaviors for
                // any component - in most cases only 1.  Using a very small
                // initial capacity so that we keep the footprint to a minimum.
                Map<String, List<ClientBehavior>> modifiableMap =
                    new HashMap<String, List<ClientBehavior>>(5,1.0f);
                behaviors = new BehaviorsMap(modifiableMap);
            }

            List<ClientBehavior> eventBehaviours = behaviors.get(eventName);

            if (null == eventBehaviours) {
                // Again using small initial capacity - we typically
                // only have 1 Behavior per event type.
                eventBehaviours = new ArrayList<ClientBehavior>(3);
                behaviors.getModifiableMap().put(eventName, eventBehaviours);
            }

            eventBehaviours.add(behavior);
        }
    }

    /**
     * <p class="changed_added_2_0">This is a default implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.
     * <code>UIComponent</code> does not implement the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
     * but provides default implementations for the methods defined by
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
     * subclass implementations.   Subclasses that wish to support the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract
     * must declare that the subclass implements
     * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must
     * override this method to return a non-Empty <code>Collection</code>
     * of the client event names that the component supports.</p>
     *
     * @since 2.0
     */
    public Collection<String> getEventNames() {
        assertClientBehaviorHolder();
        // Note: we intentionally return null here even though this
        // is not a valid value.  The result is that addClientBehavior()
        // will fail with an IllegalStateException if getEventNames()
        // is not overridden.  This should make it obvious to the
        // component author that something is wrong.
        return null;
    }

    /**
     * <p class="changed_added_2_0">This is a default implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors}.
     * <code>UIComponent</code> does not implement the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
     * but provides default implementations for the methods defined by
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
     * subclass implementations.   Subclasses that wish to support the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract
     * must declare that the subclass implements
     * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must add
     * an implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p>
     *
     * @since 2.0
     */
    public Map<String, List<ClientBehavior>> getClientBehaviors() {
        if (null == behaviors) {
            return Collections.emptyMap();
        }

        return behaviors;
    }

    /**
     * <p class="changed_added_2_0">This is a default implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName}.
     * <code>UIComponent</code> does not implement the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
     * but provides default implementations for the methods defined by
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
     * subclass implementations.   Subclasses that wish to support the
     * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract
     * must declare that the subclass implements
     * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must
     * provide an implementation of
     * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p>
     */
    public String getDefaultEventName() {
        assertClientBehaviorHolder();
        // Our default implementation just returns null - no default
        // event name;
        return null;
    }

    /**
     * {@link UIComponentBase} has stub methods from the {@link ClientBehaviorHolder} interface,
     * but these method should be used only with componets that really implement holder interface.
     * For an any other classes this method throws {@link IllegalStateException}
     * @throws IllegalStateException
     */
    private void assertClientBehaviorHolder() {
        if (!isClientBehaviorHolder()) {
            throw new IllegalStateException(
                "Attempting to use a Behavior feature with a component " +
                "that does not support any event types. "     +
                "Component must implement BehaviourHolder interface.");
        }
    }

    /**
     * @return true if component implements {@link ClientBehaviorHolder} interface.
     */
    private boolean isClientBehaviorHolder() {
        return ClientBehaviorHolder.class.isInstance(this);
    }

    /**
     * Save state of the behaviors map.
     * @param context the {@link FacesContext} for this request.
     * @return map converted to the array of <code>Object</code> or null if no behaviors have been set.
     */
    private Object saveBehaviorsState(FacesContext context){
        Object state = null;
        if (null != behaviors && behaviors.size() >0){
            boolean stateWritten = false;
            Object[] attachedBehaviors = new Object[behaviors.size()];
            int i = 0;
            for (List<ClientBehavior> eventBehaviors : behaviors.values()) {
                // we need to take different action depending on whether
                // or not markInitialState() was called.  If it's not called,
                // assume JSF 1.2 style state saving and call through to
                // saveAttachedState(), otherwise, call saveState() on the
                // behaviors directly.
                Object[] attachedEventBehaviors = new Object[eventBehaviors.size()];
                for (int j = 0; j < attachedEventBehaviors.length; j++) {
                    attachedEventBehaviors[j] = ((initialStateMarked())
                                                 ? saveBehavior(context, eventBehaviors.get(j))
                                                 : saveAttachedState(context, eventBehaviors.get(j)));
                    if (!stateWritten) {
                        stateWritten = (attachedEventBehaviors[j] != null);
                    }
                }
                attachedBehaviors[i++] = attachedEventBehaviors;
            }
            if (stateWritten) {
                state = new Object[]{behaviors.keySet().toArray(new String[behaviors.size()]),attachedBehaviors};
            }
        }
        return state;
    }

    /**
     * @param context the {@link FacesContext} for this request.
     * @param state saved state of the {@link Behavior}'s attached to the component.
     * @return restored <code>Map</code> of the behaviors.
     */
    private BehaviorsMap restoreBehaviorsState(FacesContext context, Object state) {

        if (null != state) {
            Object[] values = (Object[]) state;
            String[] names = (String[])values[0];
            Object[] attachedBehaviors = (Object[]) values[1];
            // we need to take different action depending on whether
            // or not markInitialState() was called.  If it's not called,
            // assume JSF 1.2 style state saving and call through to
            // restoreAttachedState(), otherwise, call restoreState() on the
            // behaviors directly.
            if (!initialStateMarked()) {
                Map<String, List<ClientBehavior>> modifiableMap = new HashMap<String, List<ClientBehavior>>(
                      names.length,
                      1.0f);
                for (int i = 0; i < attachedBehaviors.length; i++) {
                    Object[] attachedEventBehaviors = (Object[]) attachedBehaviors[i];
                    ArrayList<ClientBehavior> eventBehaviors =
                          new ArrayList<ClientBehavior>(attachedBehaviors.length);
                    for (int j = 0; j < attachedEventBehaviors.length; j++) {
                        eventBehaviors.add((ClientBehavior) restoreAttachedState(context,
                                                                                 attachedEventBehaviors[j]));
                    }

                    modifiableMap.put(names[i], eventBehaviors);
                }

                return new BehaviorsMap(modifiableMap);
            } else {
                for (int i = 0, len = names.length; i < len; i++) {
                    // assume the behaviors have already been populated by
                    // execution of the template.  Process the state in the
                    // same order that the names were saved.
                    List<ClientBehavior> existingBehaviors =
                          behaviors.get(names[i]);
                    restoreBehaviors(context, existingBehaviors, (Object[]) attachedBehaviors[i]);
                }
                return behaviors;
            }
        }
        return null;
    }

    private Object saveBehavior(FacesContext ctx, ClientBehavior behavior) {

        // if the Behavior isn't a StateHolder, do nothing as it will be
        // added to the BehaviorMap when the template is re-executed.
        return ((behavior instanceof StateHolder)
                ? ((StateHolder) behavior).saveState(ctx)
                : null);

    }


    private void restoreBehaviors(FacesContext ctx, List<ClientBehavior> existingBehaviors, Object[] state) {

        // this method assumes a one to one correspondence in both length and
        // order.
        for (int i = 0, len = state.length; i < len; i++) {
            ClientBehavior behavior = existingBehaviors.get(i);
            if (state[i] == null) {
                // nothing to do...move on
                continue;
            }
            // if the Behavior is a StateHolder, invoke restoreState
            // passing in the current state.  If it's not, just ignore
            // it and move along.
            if (behavior instanceof StateHolder) {
            		if (state[i] instanceof StateHolderSaver) {
            			((StateHolderSaver)state[i]).restore(ctx);
            		} else {
            			((StateHolder) behavior).restoreState(ctx, state[i]);
            		}
            }
        }
    }


    private static void publishAfterViewEvents(FacesContext context,
                                               Application application,
                                               UIComponent component) {

        component.setInView(true);
        try {
            component.pushComponentToEL(context, component);
            application.publishEvent(context, PostAddToViewEvent.class, component);
            if (component.getChildCount() > 0) {
                Collection<UIComponent> clist =
                      new ArrayList<UIComponent>(component.getChildren());
                for (UIComponent c : clist) {
                    publishAfterViewEvents(context, application, c);
                }
            }

            if (component.getFacetCount() > 0) {
                Collection<UIComponent> clist =
                      new ArrayList<UIComponent>(component.getFacets().values());
                for (UIComponent c : clist) {
                    publishAfterViewEvents(context, application, c);
                }
            }
        } finally {
            component.popComponentFromEL(context);
        }

    }


    private static void disconnectFromView(FacesContext context,
                                           Application application,
                                           UIComponent component) {

        application.publishEvent(context,
                                 PreRemoveFromViewEvent.class,
                                 component);
        component.setInView(false);
        component.compositeParent = null;
        if (component.getChildCount() > 0) {
            List<UIComponent> children = component.getChildren();
            for (UIComponent c : children) {
                disconnectFromView(context, application, c);
            }
        }
        if (component.getFacetCount() > 0) {
            Map<String, UIComponent> facets = component.getFacets();
            for (UIComponent c : facets.values()) {
                disconnectFromView(context, application, c);
            }
        }

    }


    // --------------------------------------------------------- Private Classes

    // For state saving
    private final static Object[] EMPTY_ARRAY = new Object[0];

    // Empty iterator for short circuiting operations
    private final static Iterator<UIComponent> EMPTY_ITERATOR = new Iterator<UIComponent>() {

        public void remove() {
            throw new UnsupportedOperationException();
        }

        public UIComponent next() {
            throw new NoSuchElementException("Empty Iterator");
        }

        public boolean hasNext() {
            return false;
        }
    };

    // Private implementation of Map that supports the functionality
    // required by UIComponent.getFacets()
    // HISTORY:
    //   Versions 1.333 and older used inheritence to provide the
    //     basic map functionality.  This was wasteful since a
    //     component could be completely configured via ValueExpressions
    //     or (Bindings) which means an EMPTY_OBJECT_ARRAY Map would always be
    //     present when it wasn't needed.  By using composition,
    //     we control if and when the Map is instantiated thereby
    //     reducing uneeded object allocation.  This change also
    //     has a nice side effect in state saving since we no
    //     longer need to duplicate the map, we just provide the
    //     private 'attributes' map directly to the state saving process.
    private static class AttributesMap implements Map<String, Object>, Serializable {

        // this KEY is special to the AttributesMap - this allows the implementation
        // to access the the List containing the attributes that have been set
        private static final String ATTRIBUTES_THAT_ARE_SET_KEY =
                UIComponentBase.class.getName() + ".attributesThatAreSet";

        //private Map<String, Object> attributes;
        private transient Map<String, PropertyDescriptor> pdMap;
        private transient UIComponent component;
        private static final long serialVersionUID = -6773035086539772945L;

        // -------------------------------------------------------- Constructors

        private AttributesMap(UIComponent component) {

            this.component = component;
            this.pdMap = ((UIComponentBase) component).getDescriptorMap();

        }

        public boolean containsKey(Object keyObj) {
            if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyObj)) {
                return true;
            }
            String key = (String) keyObj;
            PropertyDescriptor pd =
                    getPropertyDescriptor(key);
            if (pd == null) {
                Map<String,Object> attributes = (Map<String,Object>)
                      component.getStateHelper().get(PropertyKeys.attributes);
                if (attributes != null) {
                    return attributes.containsKey(key);
                } else {
                    return (false);
                }
            } else {
                return (false);
            }
        }

        public Object get(Object keyObj) {
            String key = (String) keyObj;
            Object result = null;
            if (key == null) {
                throw new NullPointerException();
            }
            if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
                result = component.getStateHelper().get(UIComponent.PropertyKeysPrivate.attributesThatAreSet);
            }
            Map<String,Object> attributes = (Map<String,Object>)
                  component.getStateHelper().get(PropertyKeys.attributes);
            if (null == result) {
                PropertyDescriptor pd =
                        getPropertyDescriptor(key);
                if (pd != null) {
                    try {
                        Method readMethod = pd.getReadMethod();
                        if (readMethod != null) {
                            result = (readMethod.invoke(component,
                                    EMPTY_OBJECT_ARRAY));
                        } else {
                            throw new IllegalArgumentException(key);
                        }
                    } catch (IllegalAccessException e) {
                        throw new FacesException(e);
                    } catch (InvocationTargetException e) {
                        throw new FacesException(e.getTargetException());
                    }
                } else if (attributes != null) {
                    if (attributes.containsKey(key)) {
                        result = attributes.get(key);
                    }
                }
            }
            if (null == result) {
                ValueExpression ve = component.getValueExpression(key);
                if (ve != null) {
                    try {
                        result = ve.getValue(component.getFacesContext().getELContext());
                    } catch (ELException e) {
                        throw new FacesException(e);
                    }
                }
            }

            return result;
        }

        public Object put(String keyValue, Object value) {
            if (keyValue == null) {
                throw new NullPointerException();
            }

            if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyValue)) {
                if (component.attributesThatAreSet == null) {
                    if (value instanceof List) {
                        component.getStateHelper().put(UIComponent.PropertyKeysPrivate.attributesThatAreSet,
                                                       value);
                    }
                }
                return null;
            }

            PropertyDescriptor pd =
                    getPropertyDescriptor(keyValue);
            if (pd != null) {
                try {
                    Object result = null;
                    Method readMethod = pd.getReadMethod();
                    if (readMethod != null) {
                        result = readMethod.invoke
                                (component, EMPTY_OBJECT_ARRAY);
                    }
                    Method writeMethod = pd.getWriteMethod();
                    if (writeMethod != null) {
                        writeMethod.invoke
                                (component, value);
                    } else {
                        // TODO: i18n
                        throw new IllegalArgumentException("Setter not found for property " + keyValue);
                    }
                    return (result);
                } catch (IllegalAccessException e) {
                    throw new FacesException(e);
                } catch (InvocationTargetException e) {
                    throw new FacesException
                            (e.getTargetException());
                }
            } else {
                if (value == null) {
                    throw new NullPointerException();
                }

                List<String> sProperties =
                      (List<String>) component.getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet);
                if (sProperties == null) {
                    component.getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, keyValue);
                } else if (!sProperties.contains(keyValue)) {
                    component.getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, keyValue);
                }
                return putAttribute(keyValue, value);
            }
        }

        public void putAll(Map<? extends String, ?> map) {
            if (map == null) {
                throw new NullPointerException();
            }

            for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
                this.put(entry.getKey(), entry.getValue());
            }
        }

        public Object remove(Object keyObj) {
            String key = (String) keyObj;
            if (key == null) {
                throw new NullPointerException();
            }
            if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
                return null;
            }
            PropertyDescriptor pd =
                    getPropertyDescriptor(key);
            if (pd != null) {
                throw new IllegalArgumentException(key);
            } else {
                Map<String,Object> attributes = getAttributes();
                if (attributes != null) {
                    component.getStateHelper().remove(UIComponent.PropertyKeysPrivate.attributesThatAreSet,
                                                      key);
                    return (component.getStateHelper().remove(PropertyKeys.attributes,
                                                              key));
                } else {
                    return null;
                }
            }
        }


        public int size() {
            Map attributes = getAttributes();
            return (attributes != null ? attributes.size() : 0);
        }

        public boolean isEmpty() {
            Map attributes = getAttributes();
            return (attributes == null || attributes.isEmpty());
        }

        public boolean containsValue(java.lang.Object value) {
            Map attributes= getAttributes();
            return (attributes != null && attributes.containsValue(value));
        }

        public void clear() {
            component.getStateHelper().remove(PropertyKeys.attributes);
            component.getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet);
        }

        public Set<String> keySet() {
            Map<String,Object> attributes = getAttributes();
            if (attributes != null)
                return Collections.unmodifiableSet(attributes.keySet());
            return Collections.emptySet();
        }

        public Collection<Object> values() {
            Map<String,Object> attributes = getAttributes();
            if (attributes != null)
                return Collections.unmodifiableCollection(attributes.values());
            return Collections.emptyList();
        }

        public Set<Entry<String, Object>> entrySet() {
            Map<String,Object> attributes = getAttributes();
            if (attributes != null)
                return Collections.unmodifiableSet(attributes.entrySet());
            return Collections.emptySet();
        }

        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }

            if (!(o instanceof Map)) {
                return false;
            }
            Map t = (Map) o;
            if (t.size() != size()) {
                return false;
            }

            try {
                for (Object e : entrySet()) {
                    Entry entry = (Entry) e;
                    Object key = entry.getKey();
                    Object value = entry.getValue();
                    if (value == null) {
                        if (!(t.get(key) == null && t.containsKey(key))) {
                            return false;
                        }
                    } else {
                        if (!value.equals(t.get(key))) {
                            return false;
                        }
                    }
                }
            } catch (ClassCastException unused) {
                return false;
            } catch (NullPointerException unused) {
                return false;
            }

            return true;
        }


        public int hashCode() {
            int h = 0;
            for (Object o : entrySet()) {
                h += o.hashCode();
            }
            return h;
        }

        private Map<String,Object> getAttributes() {
            return (Map<String,Object>) component.getStateHelper().get(
                  PropertyKeys.attributes);
        }

        private Object putAttribute(String key, Object value) {
            return component.getStateHelper().put(PropertyKeys.attributes,
                                                  key,
                                                  value);
        }


        /**
         * <p>Return the <code>PropertyDescriptor</code> for the specified
         * property name for this {@link UIComponent}'s implementation class,
         * if any; otherwise, return <code>null</code>.</p>
         *
         * @param name Name of the property to return a descriptor for
         * @throws FacesException if an introspection exception occurs
         */
        PropertyDescriptor getPropertyDescriptor(String name) {
            if (pdMap != null) {
                return (pdMap.get(name));
            }
            return (null);
        }

        // ----------------------------------------------- Serialization Methods

        // This is dependent on serialization occuring with in a
        // a Faces request, however, since UIComponentBase.{save,restore}State()
        // doesn't actually serialize the AttributesMap, these methods are here
        // purely to be good citizens.

        private void writeObject(ObjectOutputStream out) throws IOException {
            out.writeObject(component.getClass());
            //noinspection NonSerializableObjectPassedToObjectStream
            out.writeObject(component.saveState(FacesContext.getCurrentInstance()));
        }

        private void readObject(ObjectInputStream in)
                throws IOException, ClassNotFoundException {
            //noinspection unchecked
            Class clazz = (Class) in.readObject();
            try {
                component = (UIComponent) clazz.newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            component.restoreState(FacesContext.getCurrentInstance(), in.readObject());
        }
    }


    // Private implementation of List that supports the functionality
    // required by UIComponent.getChildren()
    private static class ChildrenList extends ArrayList<UIComponent> {

        private UIComponent component;

        public ChildrenList(UIComponent component) {
            super(6);
            this.component = component;
        }

        public void add(int index, UIComponent element) {
            if (element == null) {
                throw new NullPointerException();
            } else if ((index < 0) || (index > size())) {
                throw new IndexOutOfBoundsException();
            } else {
                eraseParent(element);
                super.add(index, element);
                element.setParent(component);

            }
        }

        public boolean add(UIComponent element) {
            if (element == null) {
                throw new NullPointerException();
            } else {
                eraseParent(element);
                boolean result = super.add(element);
                element.setParent(component);
                return result;
            }
        }

        public boolean addAll(Collection<? extends UIComponent> collection) {
            Iterator<UIComponent> elements =
                    (new ArrayList<UIComponent>(collection)).iterator();
            boolean changed = false;
            while (elements.hasNext()) {
                UIComponent element = elements.next();
                if (element == null) {
                    throw new NullPointerException();
                } else {
                    add(element);
                    changed = true;
                }
            }
            return (changed);
        }

        public boolean addAll(int index, Collection<? extends UIComponent> collection) {
            Iterator<UIComponent> elements =
                    (new ArrayList<UIComponent>(collection)).iterator();
            boolean changed = false;
            while (elements.hasNext()) {
                UIComponent element = elements.next();
                if (element == null) {
                    throw new NullPointerException();
                } else {
                    add(index++, element);
                    changed = true;
                }
            }
            return (changed);
        }

        public void clear() {
            int n = size();
            if (n < 1) {
                return;
            }
            for (int i = 0; i < n; i++) {
                UIComponent child = get(i);
                child.setParent(null);
            }
            super.clear();
        }

        public Iterator<UIComponent> iterator() {
            return (new ChildrenListIterator(this));
        }

        public ListIterator<UIComponent> listIterator() {
            return (new ChildrenListIterator(this));
        }

        public ListIterator<UIComponent> listIterator(int index) {
            return (new ChildrenListIterator(this, index));
        }

        public UIComponent remove(int index) {
            UIComponent child = get(index);
            super.remove(index);
            child.setParent(null);
            return (child);
        }

        public boolean remove(Object elementObj) {
            UIComponent element = (UIComponent) elementObj;
            if (element == null) {
                throw new NullPointerException();
            }

            if (super.remove(element)) {
                element.setParent(null);
                return (true);
            } else {
                return (false);
            }
        }

        public boolean removeAll(Collection<?> collection) {
            boolean result = false;
            for (Object elements : collection) {
                if (remove(elements)) {
                    result = true;
                }
            }
            return (result);
        }

        public boolean retainAll(Collection<?> collection) {
            boolean modified = false;
            Iterator<?> items = iterator();
            while (items.hasNext()) {
                if (!collection.contains(items.next())) {
                    items.remove();
                    modified = true;
                }
            }
            return (modified);
        }

        public UIComponent set(int index, UIComponent element) {
            if (element == null) {
                throw new NullPointerException();
            } else if ((index < 0) || (index >= size())) {
                throw new IndexOutOfBoundsException();
            } else {
                eraseParent(element);
                UIComponent previous = get(index);
                super.set(index, element);
                previous.setParent(null);
                element.setParent(component);
                return (previous);
            }
        }
    }


    // Private implementation of ListIterator for ChildrenList
    private static class ChildrenListIterator implements ListIterator<UIComponent> {


        public ChildrenListIterator(ChildrenList list) {
            this.list = list;
            this.index = 0;
        }

        public ChildrenListIterator(ChildrenList list, int index) {
            this.list = list;
            if ((index < 0) || (index > list.size())) {
                throw new IndexOutOfBoundsException(String.valueOf(index));
            } else {
                this.index = index;
            }
        }


        private ChildrenList list;
        private int index;
        private int last = -1; // Index last returned by next() or previous()

        // Iterator methods

        public boolean hasNext() {
            return (index < list.size());
        }

        public UIComponent next() {
            try {
                UIComponent o = list.get(index);
                last = index++;
                return (o);
            } catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException(String.valueOf(index));
            }
        }

        public void remove() {
            if (last == -1) {
                throw new IllegalStateException();
            }
            list.remove(last);
            if (last < index) {
                index--;
            }
            last = -1;
        }

        // ListIterator methods

        public void add(UIComponent o) {
            last = -1;
            list.add(index++, o);
        }

        public boolean hasPrevious() {
            return (index > 1);
        }

        public int nextIndex() {
            return (index);
        }

        public UIComponent previous() {
            try {
                int current = index - 1;
                UIComponent o = list.get(current);
                last = current;
                index = current;
                return (o);
            } catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException();
            }
        }

        public int previousIndex() {
            return (index - 1);
        }

        public void set(UIComponent o) {
            if (last == -1) {
                throw new IllegalStateException();
            }
            list.set(last, o);
        }

    }


    // Private implementation of Iterator for getFacetsAndChildren()
    private final static class FacetsAndChildrenIterator implements Iterator<UIComponent> {

        private Iterator<UIComponent> iterator;
        private boolean childMode;
        private UIComponent c;

        public FacetsAndChildrenIterator(UIComponent c) {
            this.c = c;
            this.childMode = false;
        }

        private void update() {
            if (this.iterator == null) {
                // we must guarantee that 'iterator' is never null
                if (this.c.getFacetCount() != 0) {
                    this.iterator = this.c.getFacets().values().iterator();
                    this.childMode = false;
                } else if (this.c.getChildCount() != 0) {
                    this.iterator = this.c.getChildren().iterator();
                    this.childMode = true;
                } else {
                    this.iterator = EMPTY_ITERATOR;
                    this.childMode = true;
                }
            } else if (!this.childMode
                    && !this.iterator.hasNext()
                    && this.c.getChildCount() != 0) {
                this.iterator = this.c.getChildren().iterator();
                this.childMode = true;
            }
        }

        public boolean hasNext() {
            this.update();
            return this.iterator.hasNext();
        }

        public UIComponent next() {
            this.update();
            return this.iterator.next();
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }


    // Private implementation of Map that supports the functionality
    // required by UIComponent.getFacets()
    private static class FacetsMap extends HashMap<String, UIComponent> {

        private UIComponent component;

        public FacetsMap(UIComponent component) {
            super(3, 1.0f);
            this.component = component;
        }

        public void clear() {
            Iterator<String> keys = keySet().iterator();
            while (keys.hasNext()) {
                keys.next();
                keys.remove();
            }
            super.clear();
        }

        public Set<Map.Entry<String, UIComponent>> entrySet() {
            return (new FacetsMapEntrySet(this));
        }

        public Set<String> keySet() {
            return (new FacetsMapKeySet(this));
        }

        public UIComponent put(String key, UIComponent value) {
            if ((key == null) || (value == null)) {
                throw new NullPointerException();
            } else //noinspection ConstantConditions
                if (!(key instanceof String) ||
                        !(value instanceof UIComponent)) {
                    throw new ClassCastException();
                }
            UIComponent previous = super.get(key);
            if (previous != null) {
                previous.setParent(null);
            }
            eraseParent(value);
            UIComponent result = super.put(key, value);
            value.setParent(component);

            return (result);
        }

        public void putAll(Map<? extends String, ? extends UIComponent> map) {
            if (map == null) {
                throw new NullPointerException();
            }
            for (Map.Entry<? extends String, ? extends UIComponent> entry : map.entrySet()) {
                put(entry.getKey(), entry.getValue());
            }
        }

        public UIComponent remove(Object key) {
            UIComponent previous = get(key);
            if (previous != null) {
                previous.setParent(null);
            }
            super.remove(key);
            return (previous);
        }

        public Collection<UIComponent> values() {
            return (new FacetsMapValues(this));
        }

        Iterator<String> keySetIterator() {
            return ((new ArrayList<String>(super.keySet())).iterator());
        }

    }


    // Private implementation of Set for FacetsMap.getEntrySet()
    private static class FacetsMapEntrySet extends AbstractSet<Map.Entry<String, UIComponent>> {

        public FacetsMapEntrySet(FacetsMap map) {
            this.map = map;
        }

        private FacetsMap map = null;

        public boolean add(Map.Entry<String, UIComponent> o) {
            throw new UnsupportedOperationException();
        }

        public boolean addAll(Collection<? extends Map.Entry<String, UIComponent>> c) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            map.clear();
        }

        public boolean contains(Object o) {
            if (o == null) {
                throw new NullPointerException();
            }
            if (!(o instanceof Map.Entry)) {
                return (false);
            }
            Map.Entry e = (Map.Entry) o;
            Object k = e.getKey();
            Object v = e.getValue();
            if (!map.containsKey(k)) {
                return (false);
            }
            if (v == null) {
                return (map.get(k) == null);
            } else {
                return (v.equals(map.get(k)));
            }
        }

        public boolean isEmpty() {
            return (map.isEmpty());
        }

        public Iterator<Map.Entry<String, UIComponent>> iterator() {
            return (new FacetsMapEntrySetIterator(map));
        }

        public boolean remove(Object o) {
            if (o == null) {
                throw new NullPointerException();
            }
            if (!(o instanceof Map.Entry)) {
                return (false);
            }
            Object k = ((Map.Entry) o).getKey();
            if (map.containsKey(k)) {
                map.remove(k);
                return (true);
            } else {
                return (false);
            }
        }

        public boolean removeAll(Collection c) {
            boolean result = false;
            for (Object element : c) {
                if (remove(element)) {
                    result = true;
                }
            }
            return (result);
        }

        public boolean retainAll(Collection c) {
            boolean result = false;
            Iterator v = iterator();
            while (v.hasNext()) {
                if (!c.contains(v.next())) {
                    v.remove();
                    result = true;
                }
            }
            return (result);
        }

        public int size() {
            return (map.size());
        }

    }


    // Private implementation of Map.Entry for FacetsMapEntrySet
    private static class FacetsMapEntrySetEntry implements Map.Entry<String, UIComponent> {

        public FacetsMapEntrySetEntry(FacetsMap map, String key) {
            this.map = map;
            this.key = key;
        }

        private FacetsMap map;
        private String key;

        public boolean equals(Object o) {
            if (o == null) {
                return (false);
            }
            if (!(o instanceof Map.Entry)) {
                return (false);
            }
            Map.Entry e = (Map.Entry) o;
            if (key == null) {
                if (e.getKey() != null) {
                    return (false);
                }
            } else {
                if (!key.equals(e.getKey())) {
                    return (false);
                }
            }
            UIComponent v = map.get(key);
            if (v == null) {
                if (e.getValue() != null) {
                    return (false);
                }
            } else {
                if (!v.equals(e.getValue())) {
                    return (false);
                }
            }
            return (true);
        }

        public String getKey() {
            return (key);
        }

        public UIComponent getValue() {
            return (map.get(key));
        }

        public int hashCode() {
            Object value = map.get(key);
            return (((key == null) ? 0 : key.hashCode()) ^
                    ((value == null) ? 0 : value.hashCode()));
        }

        public UIComponent setValue(UIComponent value) {
            UIComponent previous = map.get(key);
            map.put(key, value);
            return (previous);
        }

    }


    // Private implementation of Set for FacetsMap.getEntrySet().iterator()
    private static class FacetsMapEntrySetIterator implements Iterator<Map.Entry<String, UIComponent>> {

        public FacetsMapEntrySetIterator(FacetsMap map) {
            this.map = map;
            this.iterator = map.keySetIterator();
        }

        private FacetsMap map = null;
        private Iterator<String> iterator = null;
        private Map.Entry<String, UIComponent> last = null;

        public boolean hasNext() {
            return (iterator.hasNext());
        }

        public Map.Entry<String, UIComponent> next() {
            last = new FacetsMapEntrySetEntry(map, iterator.next());
            return (last);
        }

        public void remove() {
            if (last == null) {
                throw new IllegalStateException();
            }
            map.remove(((Map.Entry) last).getKey());
            last = null;
        }

    }


    // Private implementation of Set for FacetsMap.getKeySet()
    private static class FacetsMapKeySet extends AbstractSet<String> {

        public FacetsMapKeySet(FacetsMap map) {
            this.map = map;
        }

        private FacetsMap map = null;

        public boolean add(String o) {
            throw new UnsupportedOperationException();
        }

        public boolean addAll(Collection<? extends String> c) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            map.clear();
        }

        public boolean contains(Object o) {
            return (map.containsKey(o));
        }

        public boolean containsAll(Collection c) {
            for (Object item : c) {
                if (!map.containsKey(item)) {
                    return (false);
                }
            }
            return (true);
        }

        public boolean isEmpty() {
            return (map.isEmpty());
        }

        public Iterator<String> iterator() {
            return (new FacetsMapKeySetIterator(map));
        }

        public boolean remove(Object o) {
            if (map.containsKey(o)) {
                map.remove(o);
                return (true);
            } else {
                return (false);
            }
        }

        public boolean removeAll(Collection c) {
            boolean result = false;
            for (Object item : c) {
                if (map.containsKey(item)) {
                    map.remove(item);
                    result = true;
                }
            }
            return (result);
        }

        public boolean retainAll(Collection c) {
            boolean result = false;
            Iterator v = iterator();
            while (v.hasNext()) {
                if (!c.contains(v.next())) {
                    v.remove();
                    result = true;
                }
            }
            return (result);
        }

        public int size() {
            return (map.size());
        }

    }


    // Private implementation of Set for FacetsMap.getKeySet().iterator()
    private static class FacetsMapKeySetIterator implements Iterator<String> {

        public FacetsMapKeySetIterator(FacetsMap map) {
            this.map = map;
            this.iterator = map.keySetIterator();
        }

        private FacetsMap map = null;
        private Iterator<String> iterator = null;
        private String last = null;

        public boolean hasNext() {
            return (iterator.hasNext());
        }

        public String next() {
            last = iterator.next();
            return (last);
        }

        public void remove() {
            if (last == null) {
                throw new IllegalStateException();
            }
            map.remove(last);
            last = null;
        }

    }


    // Private implementation of Collection for FacetsMap.values()
    private static class FacetsMapValues extends AbstractCollection<UIComponent> {

        public FacetsMapValues(FacetsMap map) {
            this.map = map;
        }

        private FacetsMap map;

        public boolean add(UIComponent o) {
            throw new UnsupportedOperationException();
        }

        public boolean addAll(Collection c) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            map.clear();
        }

        public boolean isEmpty() {
            return (map.isEmpty());
        }

        public Iterator<UIComponent> iterator() {
            return (new FacetsMapValuesIterator(map));
        }

        public int size() {
            return (map.size());
        }


    }


    // Private implementation of Iterator for FacetsMap.values().iterator()
    private static class FacetsMapValuesIterator implements Iterator<UIComponent> {

        public FacetsMapValuesIterator(FacetsMap map) {
            this.map = map;
            this.iterator = map.keySetIterator();
        }

        private FacetsMap map = null;
        private Iterator<String> iterator = null;
        private Object last = null;

        public boolean hasNext() {
            return (iterator.hasNext());
        }

        public UIComponent next() {
            last = iterator.next();
            return (map.get(last));
        }

        public void remove() {
            if (last == null) {
                throw new IllegalStateException();
            }
            map.remove(last);
            last = null;
        }

    }

    // Private static member class that provide access to Behaviors.
    // Note that this Map must be unmodifiable to the external world,
    // but UIComponentBase itself needs to be able to write to the Map.
    // We solve these requirements wrapping the underlying modifiable
    // Map inside of a unmodifiable map and providing private access to
    // the underlying (modifable) Map
    private static class BehaviorsMap extends AbstractMap<String, List<ClientBehavior>>{
        private Map<String, List<ClientBehavior>> unmodifiableMap;
        private Map<String, List<ClientBehavior>> modifiableMap;

        private BehaviorsMap(Map<String, List<ClientBehavior>> modifiableMap) {
            this.modifiableMap = modifiableMap;
            this.unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
        }

        public Set<Entry<String, List<ClientBehavior>>> entrySet() {
            return unmodifiableMap.entrySet();
        }

        private Map<String, List<ClientBehavior>> getModifiableMap() {
            return modifiableMap;
        }
    }

}
