/FileExtraction/Java_unseen/dlitz_resin/modules/jsf/src/javax/faces/component/UIComponentBase.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package javax.faces.component;

import java.beans.*;
import java.lang.reflect.*;
import java.lang.annotation.Annotation;

import java.io.*;
import java.util.*;
import java.util.logging.*;

import javax.el.*;

import javax.faces.*;
import javax.faces.application.*;
import javax.faces.component.html.*;
import javax.faces.context.*;
import javax.faces.el.*;
import javax.faces.event.*;
import javax.faces.render.*;

public abstract class UIComponentBase extends UIComponent
{
  private static final UIComponent []NULL_FACETS_AND_CHILDREN
    = new UIComponent[0];
  
  private static final FacesListener []NULL_FACES_LISTENERS
    = new FacesListener[0];

  private static final HashMap<String,Integer> _rendererToCodeMap
    = new HashMap<String,Integer>();

  private static final HashMap<Integer,String> _codeToRendererMap
    = new HashMap<Integer,String>();
  
  private static final WeakHashMap<Class,HashMap<String,Property>> _compMap
    = new WeakHashMap<Class,HashMap<String,Property>>();

  private String _id;
  private String _clientId;

  private UIComponent _parent;
  
  private String _rendererType;
  private ValueExpression _rendererTypeExpr;
  
  private boolean _isTransient;
  
  private Boolean _isRendered;
  private ValueExpression _isRenderedExpr;
  
  private ValueExpression _bindingExpr;

  private ComponentList _children;
  private ComponentMap _facets;

  private UIComponent []_facetsAndChildren;

  private AttributeMap _attributeMap;
  

  
  private FacesListener []_facesListeners
    = NULL_FACES_LISTENERS;
  
  public Map<String,Object> getAttributes()
  {
    if (_attributeMap == null)
      _attributeMap = new AttributeMap(this);

    return _attributeMap;
  }
  
  /**
   * @deprecated
   */
  public ValueBinding getValueBinding(String name)
  {
    ValueExpression expr = getValueExpression(name);

    if (expr == null)
      return null;
    else if (expr instanceof ValueExpressionAdapter)
      return ((ValueExpressionAdapter) expr).getBinding();
    else
      return new ValueBindingAdapter(expr);
  }

  /**
   * @deprecated
   */
  public void setValueBinding(String name, ValueBinding binding)
  {
    setValueExpression(name, new ValueExpressionAdapter(binding));
  }

  /**
   * Returns the value expression for an attribute
   *
   * @param name the name of the attribute to get
   */
  @Override
  public ValueExpression getValueExpression(String name)
  {
    if (name == null)
      throw new NullPointerException();

    if ("rendered".equals(name))
      return _isRenderedExpr;
    else if ("rendererType".equals(name))
      return _rendererTypeExpr;
    else if ("binding".equals(name))
      return _bindingExpr;
    
    if (bindings != null)
      return bindings.get(name);
    else
      return null;
  }

  /**
   * Sets the value expression for an attribute
   *
   * @param name the name of the attribute to set
   * @param expr the value expression
   */
  @Override
  public void setValueExpression(String name, ValueExpression expr)
  {
    if (name.equals("id"))
      throw new IllegalArgumentException("'id' is not a valid ValueExpression name.");
    else if (name.equals("parent"))
      throw new IllegalArgumentException("'parent' is not a valid ValueExpression name.");
    
    if ("rendered".equals(name)) {
      if (expr.isLiteralText()) {
        _isRendered = Util.booleanValueOf(expr.getValue(null));
        return;
      }
      else
        _isRenderedExpr = expr;
    }
    else if ("rendererType".equals(name)) {
      if (expr.isLiteralText()) {
        _rendererType = String.valueOf(expr.getValue(null));
        return;
      }
      else
        _rendererTypeExpr = expr;
    }
    else if ("binding".equals(name)) {
      _bindingExpr = expr;
    }

    try {
      if (expr != null) {
        if (expr.isLiteralText()) {
          getAttributes().put(name, expr.getValue(null));
        }
        else {
          if (bindings == null)
            bindings = new HashMap<String,ValueExpression>();

          bindings.put(name, expr);
        }
      }
      else if (bindings != null)
        bindings.remove(name);
    } catch (ELException e) {
      throw new FacesException(e);
    }
  }

  /**
   * Returns the client-specific id for the component.
   */
  @Override
  public String getClientId(FacesContext context)
  {
    if (context == null)
      throw new NullPointerException();

    if (_clientId != null)
      return _clientId;

    String parentId = null;

    for (UIComponent ptr = getParent(); ptr != null; ptr = ptr.getParent()) {
      if (ptr instanceof NamingContainer) {
        parentId = ptr.getContainerClientId(context);
        break;
      }
    }

    String myId = _id;

    if (myId == null) {
      myId = context.getViewRoot().createUniqueId();
    }

    if (parentId != null)
      myId = parentId + NamingContainer.SEPARATOR_CHAR + myId;
    
    Renderer renderer = getRenderer(context);
    
    if (renderer != null)
      _clientId = renderer.convertClientId(context, myId);
    else
      _clientId = myId;

    return _clientId;
  }

  public String getId()
  {
    return _id;
  }

  public void setId(String id)
  {
    if (id == null) {
      _id = null;
      _clientId = null;
      return;
    }
    
    int len = id.length();

    if (len == 0)
      throw new IllegalArgumentException();

    char ch = id.charAt(0);

    if (! ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'))
      throw new IllegalArgumentException();

    for (int i = 1; i < len; i++) {
      ch = id.charAt(i);
      
      if (! ('a' <= ch && ch <= 'z'
             || 'A' <= ch && ch <= 'Z'
             || '0' <= ch && ch <= '9'
             || ch == '_'
             || ch == '-'))
        throw new IllegalArgumentException();
    }

    _id = id;
    _clientId = null;
  }

  public UIComponent getParent()
  {
    return _parent;
  }

  public void setParent(UIComponent parent)
  {
    _parent = parent;
  }

  public boolean isRendered()
  {
    if (_isRendered != null)
      return _isRendered;
    else if (_isRenderedExpr != null)
      return Util.evalBoolean(_isRenderedExpr, getFacesContext());
    else
      return true;
  }

  public void setRendered(boolean isRendered)
  {
    _isRendered = isRendered;
  }

  public String getRendererType()
  {
    if (_rendererType != null)
      return _rendererType;
    else if (_rendererTypeExpr != null)
      return Util.evalString(_rendererTypeExpr, getFacesContext());
    else
      return null;
  }

  public void setRendererType(String rendererType)
  {
    _rendererType = rendererType;
  }

  public boolean getRendersChildren()
  {
    Renderer renderer = getRenderer(getFacesContext());

    if (renderer != null)
      return renderer.getRendersChildren();
    else
      return false;
  }

  public List<UIComponent> getChildren()
  {
    if (_children == null)
      _children = new ComponentList(this);

    return _children;
  }

  public int getChildCount()
  {
    if (_children != null)
      return _children.size();
    else
      return 0;
  }

  public UIComponent findComponent(String expr)
  {
    UIComponent base = null;

    String []values = expr.split(":");
    
    if (values[0].equals("")) {
      for (base = this; base.getParent() != null; base = base.getParent()) {
      }
    }
    else {
      for (base = this;
           base.getParent() != null && ! (base instanceof NamingContainer);
           base = base.getParent()) {
      }
    }

    for (int i = 0; i < values.length; i++) {
      String v = values[i];

      if ("".equals(v))
        continue;

      base = findComponent(base, v);

      if (i + 1 == values.length)
        return base;

      if (base == null)
        return base;

      if (! (base instanceof NamingContainer)) {
        throw new IllegalArgumentException("'" + v + "' in expression '" + expr + "' does not match an intermediate NamingContainer.");
      }
    }
    
    return base;
  }

  private static UIComponent findComponent(UIComponent comp, String id)
  {
    if (id.equals(comp.getId()))
      return comp;

    Iterator iter = comp.getFacetsAndChildren();
    while (iter.hasNext()) {
      UIComponent child = (UIComponent) iter.next();

      if (id.equals(child.getId()))
        return child;
      
      if (! (child instanceof NamingContainer)) {
        UIComponent desc = findComponent(child, id);

        if (desc != null)
          return desc;
      }
    }

    return null;
  }

  public Map<String,UIComponent> getFacets()
  {
    if (_facets == null)
      _facets = new ComponentMap(this);

    return _facets;
  }

  public UIComponent getFacet(String name)
  {
    if (_facets != null)
      return _facets.get(name);
    else
      return null;
  }

  public Iterator<UIComponent> getFacetsAndChildren()
  {
    return new FacetAndChildIterator(getFacetsAndChildrenArray());
  }

  UIComponent []getFacetsAndChildrenArray()
  {
    if (_facetsAndChildren == null) {
      if (_children == null && _facets == null)
        _facetsAndChildren = NULL_FACETS_AND_CHILDREN;
      else {
        int facetCount = getFacetCount();
        int childCount = getChildCount();

        _facetsAndChildren = new UIComponent[facetCount + childCount];

        int i = 0;
        if (_facets != null) {
          for (UIComponent facet : _facets.values()) {
            _facetsAndChildren[i++] = facet;
          }
        }

        for (int j = 0; j < childCount; j++) {
          _facetsAndChildren[i++] = _children.get(j);
        }
      }
    }

    return _facetsAndChildren;
  }

  //
  // Listeners, broadcast and event handling
  //

  protected void addFacesListener(FacesListener listener)
  {
    if (listener == null)
      throw new NullPointerException();
    
    int length = _facesListeners.length;
    
    FacesListener[] newListeners = new FacesListener[length + 1];

    System.arraycopy(_facesListeners, 0, newListeners, 0, length);

    newListeners[length] = listener;

    _facesListeners = newListeners;
  }

  protected FacesListener []getFacesListeners(Class cl)
  {
    if (FacesListener.class.equals(cl))
      return _facesListeners;

    int count = 0;
    for (int i = _facesListeners.length - 1; i >= 0; i--) {
      if (cl.isAssignableFrom(_facesListeners[i].getClass()))
        count++;
    }

    FacesListener []array = (FacesListener []) Array.newInstance(cl, count);
    count = 0;
    for (int i = _facesListeners.length - 1; i >= 0; i--) {
      if (cl.isAssignableFrom(_facesListeners[i].getClass())) {
        array[count++] = _facesListeners[i];
      }
    }

    return array;
  }

  protected void removeFacesListener(FacesListener listener)
  {
    if (listener == null)
      throw new NullPointerException();

    int length = _facesListeners.length;
    for (int i = 0; i < length; i++) {
      if (listener.equals(_facesListeners[i])) {
        FacesListener []newListeners = new FacesListener[length - 1];
        System.arraycopy(_facesListeners, 0, newListeners, 0, i);
        System.arraycopy(_facesListeners, i + 1, newListeners, i,
                         length - i - 1);

        _facesListeners = newListeners;

        return;
      }
    }
  }

  public void queueEvent(FacesEvent event)
  {
    UIComponent parent = getParent();

    if (parent != null)
      parent.queueEvent(event);
    else
      throw new IllegalStateException();
  }

  public void broadcast(FacesEvent event)
    throws AbortProcessingException
  {
    for (int i = 0; i < _facesListeners.length; i++) {
      if (event.isAppropriateListener(_facesListeners[i])) {
        event.processListener(_facesListeners[i]);
      }
    }
  }

  //
  // decoding
  //

  /**
   * Recursively calls the decodes for any children, then calls
   * decode().
   */
  public void processDecodes(FacesContext context)
  {
    if (context == null)
      throw new NullPointerException();

    if (! isRendered())
      return;

    for (UIComponent child : getFacetsAndChildrenArray()) {
      child.processDecodes(context);
    }

    try {
      decode(context);
    } catch (RuntimeException e) {
      context.renderResponse();

      throw e;
    }
  }

  /**
   * Decodes the value of the component.
   */
  @Override
  public void decode(FacesContext context)
  {
    Renderer renderer = getRenderer(context);

    if (renderer != null)
      renderer.decode(context, this);
  }

  //
  // Validation
  //

  @Override
  public void processValidators(FacesContext context)
  {
    if (context == null)
      throw new NullPointerException();

    if (! isRendered())
      return;

    for (UIComponent child : getFacetsAndChildrenArray()) {
      child.processValidators(context);
    }
  }

  //
  // Model updates
  //

  public void processUpdates(FacesContext context)
  {
    if (context == null)
      throw new NullPointerException();

    if (! isRendered())
      return;

    for (UIComponent child : getFacetsAndChildrenArray()) {
      child.processUpdates(context);
    }
  }

  //
  // Encoding
  //

  /**
   * Starts the output rendering for the encoding.
   */
  public void encodeBegin(FacesContext context)
    throws IOException
  {
    if (context == null)
      throw new NullPointerException();

    if (! isRendered())
      return;

    try {
      Renderer renderer = getRenderer(context);

      if (renderer != null)
        renderer.encodeBegin(context, this);

    } catch (IOException e) {
      if (e.getMessage().startsWith("id="))
        throw e;
      else
        throw new IOExceptionWrapper("id=" + getClientId(context)
                                     + " " + e.toString(), e);
    } catch (RuntimeException e) {
      if (e.getMessage() != null
          && e.getMessage().startsWith("id="))
        throw e;
      else
        throw new FacesException("id=" + getClientId(context)
                                 + " " + e.toString(), e);
    }
  }

  public void encodeChildren(FacesContext context)
    throws IOException
  {
    if (context == null)
      throw new NullPointerException();

    if (! isRendered())
      return;

    Renderer renderer = getRenderer(context);

    if (renderer != null)
      renderer.encodeChildren(context, this);
  }

  public void encodeEnd(FacesContext context)
    throws IOException
  {
    if (context == null)
      throw new NullPointerException();

    if (! isRendered())
      return;

    Renderer renderer = getRenderer(context);

    if (renderer != null)
      renderer.encodeEnd(context, this);
  }

  @Override
  protected FacesContext getFacesContext()
  {
    return FacesContext.getCurrentInstance();
  }

  @Override
  protected Renderer getRenderer(FacesContext context)
  {
    String rendererType = getRendererType();

    if (rendererType == null)
      return null;
    
    RenderKit renderKit = context.getRenderKit();

    if (renderKit != null)
      return renderKit.getRenderer(getFamily(), getRendererType());
    else
      return null;
  }

  //
  // Save the state of the component
  //

  public Object processSaveState(FacesContext context)
  {
    if (context == null)
      throw new NullPointerException();

    if (isTransient())
      return null;
    
    UIComponent []facetsAndChildren = getFacetsAndChildrenArray();

    Object []childSaveState = null;

    int childSize = getChildCount();
    int facetSize = getFacetCount();
    int k = 1;
      
    if (childSize > 0) {
      List<UIComponent> children = getChildren();
      
      for (int i = 0; i < childSize; i++) {
        UIComponent child = children.get(i);

        if (child.isTransient())
          continue;

        k++;

        Object childState = child.processSaveState(context);

        if (childState != null) {
          if (childSaveState == null)
            childSaveState = new Object[1 + childSize + 2 * facetSize];
      
          childSaveState[k - 1] = childState;
        }
      }
    }

    if (facetSize > 0) {
      Map<String,UIComponent> facetMap = getFacets();

      for (Map.Entry<String,UIComponent> entry : facetMap.entrySet()) {
        UIComponent facet = entry.getValue();

        if (facet.isTransient())
          continue;

        k += 2;

        Object facetState = facet.processSaveState(context);

        if (facetState != null) {
          if (childSaveState == null)
            childSaveState = new Object[1 + childSize + 2 * facetSize];
      
          childSaveState[k - 2] = entry.getKey();
          childSaveState[k - 1] = facetState;
        }
      }
    }

    Object selfSaveState = saveState(context);

    if (childSaveState != null) {
      childSaveState[0] = selfSaveState;
      return childSaveState;
    }
    else
      return new Object[] { selfSaveState };
  }

  public void processRestoreState(FacesContext context,
                                  Object state)
  {
    if (context == null)
      throw new NullPointerException();

    if (isTransient())
      return;

    UIComponent []facetsAndChildren = getFacetsAndChildrenArray();

    Object []baseState = (Object []) state;

    if (baseState == null)
      return;

    restoreState(context, baseState[0]);

    if (baseState.length == 1)
      return;

    int childSize = getChildCount();
    int facetSize = getFacetCount();
    int k = 1;
      
    if (childSize > 0) {
      List<UIComponent> children = getChildren();
      
      for (int i = 0; i < childSize; i++) {
        UIComponent child = children.get(i);

        if (child.isTransient()) {
          continue;
        }

        k++;

        Object childState;

        if (k <= baseState.length)
          childState = baseState[k - 1];
        else
          childState = null;

        if (childState != null)
          child.processRestoreState(context, childState);
      }
    }
      
    if (facetSize > 0) {
      Map<String,UIComponent> facetMap = getFacets();

      for (; k < baseState.length; k += 2) {
        String facetName = (String) baseState[k];
        Object facetState = baseState[k + 1];

        if (facetName != null && facetState != null) {
          UIComponent facet = facetMap.get(facetName);

          if (facet != null)
            facet.processRestoreState(context, facetState);
        }
      }
    }
  }
  
  public Object saveState(FacesContext context)
  {
    Integer rendererCode = _rendererToCodeMap.get(_rendererType);
    String rendererString = null;

    if (rendererCode == null)
      rendererString = _rendererType;

    Object []savedListeners = saveListeners(context);

    Object []savedBindings = saveBindings(context);
    
    return new Object[] {
      _id,
      savedBindings,
      _isRendered,
      rendererCode,
      rendererString,
      (_attributeMap != null ? _attributeMap.saveState(context) : null),
      savedListeners,
    };
  }

  private Object []saveBindings(FacesContext context) {
    if (bindings == null)
      return null;

    Set<String> keys = bindings.keySet();
    Object [] result = new Object [keys.size() * 2];

    int index = 0;
    for (String key : keys) {
      result [index++] = key;

      ValueExpression valueExpression = bindings.get(key);

      result [index++] = saveAttachedState(context, valueExpression);
    }

    return result;
  }

  private void restoreBindings(FacesContext context, Object stateObj) {
    Object [] state = (Object []) stateObj;

    if (state.length == 0) return;

    bindings = new HashMap<String, ValueExpression>();


    for (int i = 0; i < state.length / 2; i++) {
      int index = i * 2;

      String key = (String) state[index];

      ValueExpression valueExpression 
        = (ValueExpression) restoreAttachedState(context, state [index + 1]);

      bindings.put(key, valueExpression);
    }
  }

  private Object []saveListeners(FacesContext context)
  {
    if (_facesListeners.length > 0) {
      Object []savedListeners = new Object[2 * _facesListeners.length];

      for (int i = 0; i < _facesListeners.length; i++) {
        FacesListener listener = _facesListeners[i];

        int index = 2 * i;

        if (listener instanceof java.io.Serializable) {
          savedListeners[index] = java.io.Serializable.class;
          savedListeners[index + 1] = listener;
        } else if (listener instanceof StateHolder) {
          savedListeners[index] = listener.getClass();
          StateHolder holder = (StateHolder) listener;
          savedListeners[index + 1] = holder.saveState(context);
        } else {
          savedListeners[index] = listener.getClass();
        }
      }

      return savedListeners;
    }

    return null;
  }

  public void restoreState(FacesContext context, Object stateObj)
  {
    Object []state = (Object []) stateObj;

    _id = (String) state[0];

    Object []savedBindings = (Object[]) state[1];

    if (savedBindings != null)
      restoreBindings(context, savedBindings);

    if (bindings != null) {
      for (Map.Entry<String,ValueExpression> entry : bindings.entrySet()) {
        setValueExpression(entry.getKey(), entry.getValue());
      }
    }

    _isRendered = (Boolean) state[2];

    Integer rendererCode = (Integer) state[3];
    String rendererString = (String) state[4];

    if (rendererCode != null)
      _rendererType = _codeToRendererMap.get(rendererCode);
    else
      _rendererType = rendererString;

    Object extMapState = state[5];

    if (extMapState != null) {
      if (_attributeMap == null)
        _attributeMap = new AttributeMap(this);

      _attributeMap.restoreState(context, extMapState);
    }

    Object []savedListeners = (Object []) state[6];

    restoreListeners(context, savedListeners);
  }

  private void restoreListeners(FacesContext context, Object[] savedListeners)
  {
    if (savedListeners != null) {
      _facesListeners = new FacesListener[savedListeners.length / 2];

      for (int i = 0; i < _facesListeners.length; i++) {
        int index = 2 * i;

        Class cl = (Class) savedListeners[index];

        try {
          if (java.io.Serializable.class.equals(cl)) {
            _facesListeners[i] = (FacesListener) savedListeners[index + 1];
          }
          else {
            FacesListener listener = (FacesListener) cl.newInstance();

            if (listener instanceof StateHolder) {
              StateHolder holder = (StateHolder) listener;

              holder.restoreState(context, savedListeners[index + 1]);
            }
            _facesListeners[i] = listener;
          }
        }
        catch (Exception e) {
          throw new FacesException(e);
        }
      }
    }
  }

  private Object saveExprMap(FacesContext context,
                             Map<String,ValueExpression> exprMap)
  {
    if (exprMap == null)
      return null;

    int size = exprMap.size();
    
    Object []values = new Object[3 * size];

    int i = 0;
    for (Map.Entry<String,ValueExpression> entry : exprMap.entrySet()) {
      values[i++] = entry.getKey();

      ValueExpression expr = entry.getValue();
      values[i++] = expr.getExpressionString();
      values[i++] = expr.getExpectedType();
    }

    return values;
  }

  private HashMap<String,ValueExpression>
    restoreExprMap(FacesContext context, Object value)
  {
    if (value == null)
      return null;

    Object []state = (Object[]) value;

    HashMap<String,ValueExpression> map
      = new HashMap<String,ValueExpression>();
    
    Application app = context.getApplication();
    ExpressionFactory exprFactory = app.getExpressionFactory();

    int i = 0;
    while (i < state.length) {
      String key = (String) state[i++];
      String exprString = (String) state[i++];
      Class type = (Class) state[i++];

      ValueExpression expr
        = exprFactory.createValueExpression(context.getELContext(),
                                            exprString,
                                            type);

      map.put(key, expr);
    }

    return map;
  }

  public void setTransient(boolean isTransient)
  {
    _isTransient = isTransient;
  }

  public boolean isTransient()
  {
    return _isTransient;
  }

  private void removeChild(UIComponent child)
  {
    if (_children != null) {
      if (_children.remove(child))
        return;
    }
    
    if (_facets != null) {
      for (Map.Entry<String,UIComponent> entry : _facets.entrySet()) {
        if (entry.getValue() == child) {
          _facets.remove(entry.getKey());
          return;
        }
      }
    }
  }

  public static Object saveAttachedState(FacesContext context,
                                         Object attachedObject)
  {
    if (attachedObject == null)
      return null;
    else if (attachedObject instanceof List) {
      List list = (List) attachedObject;

      ArrayList values = new ArrayList();
      int len = list.size();
      
      for (int i = 0; i < len; i++) {
        values.add(saveAttachedState(context, list.get(i)));
      }

      return values;
    }
    else if (attachedObject instanceof StateHolder)
      return new StateHandle(context, attachedObject);
    else if (attachedObject instanceof Serializable)
      return attachedObject;
    else
      return new StateHandle(context, attachedObject);
  }

  public static Object restoreAttachedState(FacesContext context,
                                            Object stateObject)
  {
    if (stateObject == null)
      return null;
    else if (stateObject instanceof List) {
      List list = (List) stateObject;
      
      ArrayList values = new ArrayList();
      int size = list.size();

      for (int i = 0; i < size; i++) {
        values.add(restoreAttachedState(context, list.get(i)));
      }
      
      return values;
    }
    else if (stateObject instanceof StateHandle)
      return ((StateHandle) stateObject).restore(context);
    else
      return stateObject;
  }
  
  private static class StateHandle implements java.io.Serializable {
    private Class _class;
    private Object _state;

    public StateHandle()
    {
    }

    public StateHandle(FacesContext context, Object value)
    {
      _class = value.getClass();

      if (value instanceof StateHolder)
        _state = ((StateHolder) value).saveState(context);
    }

    public Object restore(FacesContext context)
    {
      try {
        Object value = _class.newInstance();

        if (value instanceof StateHolder)
          ((StateHolder) value).restoreState(context, _state);

        return value;
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  private static class ComponentList extends AbstractList<UIComponent>
    implements java.io.Serializable
  {
    private ArrayList<UIComponent> _list = new ArrayList<UIComponent>();
    
    private UIComponentBase _parent;

    ComponentList(UIComponentBase parent)
    {
      _parent = parent;
    }

    @Override
    public boolean add(UIComponent child)
    {
      setParent(child);

      _parent._facetsAndChildren = null;

      boolean result = _list.add(child);

      FacesContext context = FacesContext.getCurrentInstance();

      return result;
    }

    @Override
    public void add(int i, UIComponent child)
    {
      _list.add(i, child);

      setParent(child);

      _parent._facetsAndChildren = null;
    }

    @Override
    public boolean addAll(int i, Collection<? extends UIComponent> list)
    {
      boolean isChange = false;
      
      for (UIComponent child : list) {
        setParent(child);

        _list.add(i++, child);

        isChange = true;
      }

      _parent._facetsAndChildren = null;
      
      return isChange;
    }

    private boolean isPostback(FacesContext context) {
      RenderKitFactory factory
        = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);

      String renderKitId = context.getViewRoot().getRenderKitId();

      RenderKit renderKit = factory.getRenderKit(context, renderKitId);

      if (renderKit == null)
        renderKit = factory.getRenderKit(context, RenderKitFactory.HTML_BASIC_RENDER_KIT);
      
      return renderKit.getResponseStateManager().isPostback(context);
    }

    @Override
    public UIComponent set(int i, UIComponent child)
    {
      UIComponent old = _list.remove(i);

      if (old != null)
        old.setParent(null);

      setParent(child);

      _list.add(i, child);

      _parent._facetsAndChildren = null;
      
      return old;
    }

    @Override
    public UIComponent remove(int i)
    {
      UIComponent old = _list.remove(i);

      if (old != null) {
        old.setParent(null);
      }

      _parent._facetsAndChildren = null;
      
      return old;
    }

    @Override
    public boolean remove(Object v)
    {
      UIComponent comp = (UIComponent) v;
      
      if (_list.remove(comp)) {
        comp.setParent(null);

        _parent._facetsAndChildren = null;
      
        return true;
      }
      else
        return false;
    }

    @Override
    public UIComponent get(int i)
    {
      return _list.get(i);
    }

    private void setParent(UIComponent child)
    {
      UIComponent parent = child.getParent();

      if (parent == null) {
      }
      else if (parent instanceof UIComponentBase) {
        ((UIComponentBase) parent).removeChild(child);
      }
      else {
        parent.getChildren().remove(child);
      }

      child.setParent(_parent);
    }

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

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

    public Iterator<UIComponent> iterator()
    {
      return _list.iterator();
    }
  }

  public String toString()
  {
    return getClass().getName() + "[" + getId() + "]";
  }

  private static class ComponentMap extends HashMap<String,UIComponent>
  {
    private UIComponentBase _parent;

    ComponentMap(UIComponentBase parent)
    {
      _parent = parent;
    }

    @Override
    public UIComponent put(String key, UIComponent o)
    {
      if (key == null)
        throw new NullPointerException();

      _parent._facetsAndChildren = null;
      
      UIComponent child = o;

      UIComponent parent = child.getParent();
      if (parent instanceof UIComponentBase) {
        ((UIComponentBase) parent).removeChild(child);
      }

      child.setParent(_parent);

      UIComponent oldChild = super.put(key, o);

      if (oldChild != null && oldChild != o) {
        oldChild.setParent(null);
      }

      return oldChild;
    }

    @Override
    public UIComponent remove(Object key)
    {
      if (key == null)
        throw new NullPointerException();

      _parent._facetsAndChildren = null;

      UIComponent oldChild = super.remove(key);

      if (oldChild != null) {
        oldChild.setParent(null);
      }

      return oldChild;
    }
  }

  private static class FacetAndChildIterator
    implements Iterator<UIComponent> {
    private final UIComponent []_children;
    private int _index;

    FacetAndChildIterator(UIComponent []children)
    {
      _children = children;
    }

    public boolean hasNext()
    {
      return _index < _children.length;
    }

    public UIComponent next()
    {
      if (_index < _children.length)
        return _children[_index++];
      else
        return null;
    }

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

  private static class AttributeMap extends AbstractMap<String,Object>
    implements Serializable
  {
    private final transient HashMap<String,Property> _propertyMap;
    private HashMap<String,Object> _extMap;
    private Object _obj;

    AttributeMap(Object obj)
    {
      _obj = obj;
      
      Class cl = obj.getClass();
      
      synchronized (cl) {
        HashMap<String,Property> propMap = _compMap.get(cl);

        if (propMap == null) {
          propMap = introspectComponent(cl);
          _compMap.put(cl, propMap);
        }
      
        _propertyMap = propMap;
      }
    }

    Object saveState(FacesContext context)
    {
      return _extMap;
    }

    void restoreState(FacesContext context, Object state)
    {
      _extMap = (HashMap<String,Object>) state;
    }

    public boolean containsKey(String name)
    {
      Property prop = _propertyMap.get(name);

      if (prop != null)
        return false;
      else if (_extMap != null)
        return _extMap.containsKey(name);
      else
        return false;
    }

    @Override
    public Object get(Object v)
    {
      String name = (String) v;
      
      Property prop = _propertyMap.get(name);

      if (prop == null) {
        if (_extMap != null)
          return _extMap.get(name);
        else {
          // XXX: ValueExpression?
          return null;
        }
      }

      Method getter = prop.getGetter();
      
      if (getter == null)
        throw new IllegalArgumentException(name + " is not readable");

      try {
        return getter.invoke(_obj);
      } catch (InvocationTargetException e) {
        throw new FacesException(e.getCause());
      } catch (Exception e) {
        throw new FacesException(e);
      }
    }

    @Override
    public Object put(String name, Object value)
    {
      if (name == null || value == null)
        throw new NullPointerException();

      Property prop = _propertyMap.get(name);

      if (prop == null) {
        if (_extMap == null)
          _extMap = new HashMap<String,Object>(8);

        return _extMap.put(name, value);
      }

      if (prop.getSetter()  == null)
        throw new IllegalArgumentException(name + " is not writable");

      try {
        return prop.getSetter().invoke(_obj, value);
      } catch (Exception e) {
        throw new FacesException(e);
      }
    }

    @Override
    public Object remove(Object name)
    {
      Property prop = _propertyMap.get(name);

      if (prop == null) {
        if (_extMap != null)
          return _extMap.remove(name);
        else
          return null;
      }

      throw new IllegalArgumentException(name + " cannot be removed");
    }

    public Set<Map.Entry<String,Object>> entrySet()
    {
      if (_extMap != null)
        return _extMap.entrySet();
      else
        return Collections.EMPTY_SET;
    }

    private static HashMap<String,Property> introspectComponent(Class cl)
    {
      HashMap<String,Property> map = new HashMap<String,Property>();

      try {
        BeanInfo info = Introspector.getBeanInfo(cl, Object.class);

        for (PropertyDescriptor propDesc : info.getPropertyDescriptors()) {
            Property prop = new Property(propDesc.getReadMethod(),
                                       propDesc.getWriteMethod());

          map.put(propDesc.getName(), prop);
        }
      } catch (Exception e) {
        throw new FacesException(e);
      }

      return map;
    }
  }

  private static class Property {
    private final Method _getter;
    private final Method _setter;

    Property(Method getter, Method setter)
    {
      _getter = getter;
      _setter = setter;
    }

    public Method getGetter()
    {
      return _getter;
    }

    public Method getSetter()
    {
      return _setter;
    }
  }

  private static class ValueExpressionAdapter
    extends ValueExpression
    implements StateHolder
  {
    private ValueBinding _binding;
    private boolean _isTransient;

    ValueExpressionAdapter()
    {
    }

    ValueExpressionAdapter(ValueBinding binding)
    {
      _binding = binding;
    }

    ValueBinding getBinding()
    {
      return _binding;
    }

    public Object getValue(ELContext elContext)
    {
      return _binding.getValue(FacesContext.getCurrentInstance());
    }

    public void setValue(ELContext elContext, Object value)
    {
      _binding.setValue(FacesContext.getCurrentInstance(), value);
    }

    public boolean isReadOnly(ELContext elContext)
    {
      return _binding.isReadOnly(FacesContext.getCurrentInstance());
    }

    public Class getType(ELContext elContext)
    {
      return _binding.getType(FacesContext.getCurrentInstance());
    }

    public Class getExpectedType()
    {
      return Object.class;
    }

    public boolean isLiteralText()
    {
      return false;
    }

    public int hashCode()
    {
      return _binding.getExpressionString().hashCode();
    }

    public boolean equals(Object o)
    {
      if (! (o instanceof ValueExpression))
        return false;

      ValueExpression expr = (ValueExpression) o;
      
      return getExpressionString().equals(expr.getExpressionString());
    }

    public String getExpressionString()
    {
      return _binding.getExpressionString();
    }

    public Object saveState(FacesContext context)
    {
      return saveAttachedState(context, _binding);
    }

    public void restoreState(FacesContext context, Object state)
    {
      _binding = (ValueBinding) restoreAttachedState(context, state);
    }

    public boolean isTransient()
    {
      return _isTransient;
    }

    public void setTransient(boolean isTransient)
    {
      _isTransient = isTransient;
    }

    public String toString()
    {
      return "ValueExpressionAdapter[" + getExpressionString() + "]";
    }
  }

  private static class ValueBindingAdapter extends ValueBinding
  {
    private final ValueExpression _expr;

    ValueBindingAdapter(ValueExpression expr)
    {
      _expr = expr;
    }

    public Object getValue(FacesContext context)
      throws EvaluationException
    {
      return _expr.getValue(context.getELContext());
    }

    public void setValue(FacesContext context, Object value)
      throws EvaluationException
    {
      _expr.setValue(context.getELContext(), value);
    }

    public boolean isReadOnly(FacesContext context)
      throws EvaluationException
    {
      return _expr.isReadOnly(context.getELContext());
    }

    public Class getType(FacesContext context)
      throws EvaluationException
    {
      return _expr.getType(context.getELContext());
    }

    public String getExpressionString()
    {
      return _expr.getExpressionString();
    }

    public String toString()
    {
      return "ValueBindingAdapter[" + _expr + "]";
    }
  }

  static class IOExceptionWrapper extends IOException {
    private Throwable _cause;

    IOExceptionWrapper(String msg, Throwable cause)
    {
      super(msg);

      _cause = cause;
    }

    public Throwable getCause()
    {
      return _cause;
    }
  }

  private static final void addRendererCode(String renderer)
  {
    if (renderer == null || _rendererToCodeMap.get(renderer) != null)
      return;
    
    Integer code = _rendererToCodeMap.size() + 1;
    
    _rendererToCodeMap.put(renderer, code);
    _codeToRendererMap.put(code, renderer);
  }
  
  static {
    addRendererCode(new UIColumn().getRendererType());
    addRendererCode(new UICommand().getRendererType());
    addRendererCode(new UIData().getRendererType());
    addRendererCode(new UIForm().getRendererType());
    addRendererCode(new UIGraphic().getRendererType());
    addRendererCode(new UIInput().getRendererType());
    addRendererCode(new UIMessage().getRendererType());
    addRendererCode(new UIMessages().getRendererType());
    addRendererCode(new UINamingContainer().getRendererType());
    addRendererCode(new UIOutput().getRendererType());
    addRendererCode(new UIPanel().getRendererType());
    addRendererCode(new UIParameter().getRendererType());
    addRendererCode(new UISelectBoolean().getRendererType());
    addRendererCode(new UISelectItem().getRendererType());
    addRendererCode(new UISelectItems().getRendererType());
    addRendererCode(new UISelectMany().getRendererType());
    addRendererCode(new UISelectOne().getRendererType());
    addRendererCode(new UIViewRoot().getRendererType());
    
    addRendererCode(new HtmlColumn().getRendererType());
    addRendererCode(new HtmlCommandButton().getRendererType());
    addRendererCode(new HtmlCommandLink().getRendererType());
    addRendererCode(new HtmlDataTable().getRendererType());
    addRendererCode(new HtmlForm().getRendererType());
    addRendererCode(new HtmlGraphicImage().getRendererType());
    addRendererCode(new HtmlInputHidden().getRendererType());
    addRendererCode(new HtmlInputSecret().getRendererType());
    addRendererCode(new HtmlInputText().getRendererType());
    addRendererCode(new HtmlInputTextarea().getRendererType());
    addRendererCode(new HtmlMessage().getRendererType());
    addRendererCode(new HtmlMessages().getRendererType());
    addRendererCode(new HtmlOutputFormat().getRendererType());
    addRendererCode(new HtmlOutputLabel().getRendererType());
    addRendererCode(new HtmlOutputLink().getRendererType());
    addRendererCode(new HtmlOutputText().getRendererType());
    addRendererCode(new HtmlPanelGrid().getRendererType());
    addRendererCode(new HtmlPanelGroup().getRendererType());
    addRendererCode(new HtmlSelectBooleanCheckbox().getRendererType());
    addRendererCode(new HtmlSelectManyCheckbox().getRendererType());
    addRendererCode(new HtmlSelectManyListbox().getRendererType());
    addRendererCode(new HtmlSelectManyMenu().getRendererType());
    addRendererCode(new HtmlSelectOneListbox().getRendererType());
    addRendererCode(new HtmlSelectOneMenu().getRendererType());
    addRendererCode(new HtmlSelectOneRadio().getRendererType());
  }
}
