All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.component.UIComponentBase Maven / Gradle / Ivy

Go to download

Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 4.1.0
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package javax.faces.component;

import static com.sun.faces.util.Util.isAllNull;
import static com.sun.faces.util.Util.isAnyNull;
import static com.sun.faces.util.Util.isEmpty;
import static java.beans.Introspector.getBeanInfo;
import static java.lang.Boolean.TRUE;
import static java.lang.Character.isDigit;
import static java.lang.Character.isLetter;
import static java.lang.Thread.currentThread;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableList;
import static java.util.logging.Level.FINE;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;

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.AbortProcessingException;
import javax.faces.event.BehaviorEvent;
import javax.faces.event.ComponentSystemEventListener;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.event.PostAddToViewEvent;
import javax.faces.event.PostValidateEvent;
import javax.faces.event.PreRemoveFromViewEvent;
import javax.faces.event.PreRenderComponentEvent;
import javax.faces.event.PreValidateEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.render.Renderer;

import com.sun.faces.application.ValueBindingValueExpressionAdapter;
import com.sun.faces.application.ValueExpressionValueBindingAdapter;

/**
 * 

* UIComponentBase is a convenience base class that implements the * default concrete behavior of all methods defined by {@link UIComponent}. *

* *

* By default, this class defines getRendersChildren() to find the renderer for this * component and call its getRendersChildren() method. The default implementation on * the Renderer returns false. As of version 1.2 of the JavaServer Faces * Specification, component authors are encouraged to return true 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 true instead. *

*/ 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"; private static final int MY_STATE = 0; private static final int CHILD_STATE = 1; /** *

* Each entry is an map of PropertyDescriptors describing the properties of a * concrete {@link UIComponent} implementation, keyed by the corresponding * java.lang.Class. *

*

*/ private Map, Map> descriptors; /** * Reference to the map of PropertyDescriptors for this class in the * descriptors Map. */ private Map propertyDescriptorMap; private Map, List> listenersByEventClass; /** *

* An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods. *

*/ private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0]; /** *

* The Map containing our attributes, keyed by attribute name. *

*/ private AttributesMap attributes; /** *

* The component identifier for this component. *

*/ private String id; /** *

* The assigned client identifier for this component. *

*/ private String clientId; /** *

* The parent component for this component. *

*/ private UIComponent parent; /** * The List containing our child components. */ private List children; /** * The Map containing our related facet components. */ private Map facets; private AttachedObjectListHolder listeners; /** * Flag indicating a desire to now participate in state saving. */ private boolean transientFlag; public UIComponentBase() { populateDescriptorsMapIfNecessary(); } @Override public Map getAttributes() { if (attributes == null) { attributes = new AttributesMap(this); } return attributes; } @Override public Map getPassThroughAttributes(boolean create) { @SuppressWarnings("unchecked") Map passThroughAttributes = (Map) this.getStateHelper().get(PropertyKeys.passThroughAttributes); if (passThroughAttributes == null && create) { passThroughAttributes = new PassThroughAttributesMap<>(); getStateHelper().put(PropertyKeys.passThroughAttributes, passThroughAttributes); } return passThroughAttributes; } // -------------------------------------------------------------- Properties /** * @throws NullPointerException {@inheritDoc} */ @Override public String getClientId(FacesContext context) { if (context == null) { throw new NullPointerException(); } // If the clientId is not yet set if (clientId == null) { UIComponent namingContainerAncestor = getNamingContainerAncestor(); // Give the parent the opportunity to first grab a unique clientId String parentId = getParentId(context, namingContainerAncestor); // Now resolve our own client id clientId = getId(); if (clientId == null) { setId(generateId(context, namingContainerAncestor)); clientId = getId(); } if (parentId != null) { clientId = addParentId(context, parentId, clientId); } // Allow the renderer to convert the clientId Renderer renderer = getRenderer(context); if (renderer != null) { clientId = renderer.convertClientId(context, clientId); } } return clientId; } @Override public String getId() { return id; } /** * @throws IllegalArgumentException {@inheritDoc} * @throws IllegalStateException {@inheritDoc} */ @Override 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 } @Override public UIComponent getParent() { return parent; } @Override 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 (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. getAttributes().put(ADDED, TRUE); doPostAddProcessing(FacesContext.getCurrentInstance(), this); // Remove the attribute once we've returned from the event // processing. this.getAttributes().remove(ADDED); } } } @Override public boolean isRendered() { return Boolean.valueOf(getStateHelper().eval(PropertyKeys.rendered, TRUE).toString()); } @Override public void setRendered(boolean rendered) { getStateHelper().put(PropertyKeys.rendered, rendered); } @Override public String getRendererType() { return (String) getStateHelper().eval(PropertyKeys.rendererType); } @Override public void setRendererType(String rendererType) { getStateHelper().put(PropertyKeys.rendererType, rendererType); } @Override public boolean getRendersChildren() { if (getRendererType() != null) { Renderer renderer = getRenderer(getFacesContext()); if (renderer != null) { return renderer.getRendersChildren(); } } return false; } // ------------------------------------------------- Tree Management Methods @Override public List getChildren() { if (children == null) { children = new ChildrenList(this); } return children; } // Do not allocate the children List to answer this question @Override public int getChildCount() { if (children != null) { return children.size(); } return 0; } /** * @throws NullPointerException {@inheritDoc} */ @Override public UIComponent findComponent(String expression) { if (expression == null) { throw new NullPointerException(); } if (expression.isEmpty()) { // If an empty value is provided, fail fast. throw new IllegalArgumentException("\"\""); } final char sepChar = UINamingContainer.getSeparatorChar(FacesContext.getCurrentInstance()); // Identify the base component from which we will perform our search UIComponent base = findBaseComponent(expression, sepChar); if (expression.charAt(0) == sepChar) { // Treat remainder of the expression as relative expression = expression.substring(1); } // Evaluate the search expression (now guaranteed to be relative) return evaluateSearchExpression(base, expression, String.valueOf(sepChar)); } /** * {@inheritDoc} * * @throws NullPointerException {@inheritDoc} * @throws FacesException {@inheritDoc} * @since 1.2 */ @Override public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException { return super.invokeOnComponent(context, clientId, callback); } // ------------------------------------------------ Facet Management Methods @Override public Map getFacets() { if (facets == null) { facets = new FacetsMap(this); } return facets; } // Do not allocate the children List to answer this question @Override public int getFacetCount() { if (facets != null) { return facets.size(); } return 0; } // Do not allocate the facets Map to answer this question @Override public UIComponent getFacet(String name) { if (facets != null) { return facets.get(name); } return null; } @Override public Iterator getFacetsAndChildren() { int childCount = getChildCount(), facetCount = getFacetCount(); // If there are neither facets nor children if (childCount == 0 && facetCount == 0) { return EMPTY_ITERATOR; } // If there are only facets and no children if (childCount == 0) { return unmodifiableCollection(getFacets().values()).iterator(); } // If there are only children and no facets if (facetCount == 0) { return unmodifiableList(getChildren()).iterator(); } // If there are both children and facets return new FacetsAndChildrenIterator(this); } // -------------------------------------------- Lifecycle Processing Methods /** * @throws AbortProcessingException {@inheritDoc} * @throws IllegalStateException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ @Override 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} */ @Override public void decode(FacesContext context) { if (context == null) { throw new NullPointerException(); } String rendererType = getRendererType(); if (rendererType != null) { Renderer renderer = getRenderer(context); if (renderer != null) { renderer.decode(context, this); } else { if (LOGGER.isLoggable(FINE)) { LOGGER.fine("Can't get Renderer for type " + rendererType); } } } } /** * @throws NullPointerException {@inheritDoc} */ @Override 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 = getRenderer(context); if (renderer != null) { renderer.encodeBegin(context, this); } else { if (LOGGER.isLoggable(FINE)) { LOGGER.fine("Can't get Renderer for type " + rendererType); } } } } /** * @throws NullPointerException {@inheritDoc} */ @Override public void encodeChildren(FacesContext context) throws IOException { if (context == null) { throw new NullPointerException(); } if (!isRendered()) { return; } if (getRendererType() != null) { Renderer renderer = 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} */ @Override public void encodeEnd(FacesContext context) throws IOException { if (context == null) { throw new NullPointerException(); } if (!isRendered()) { popComponentFromEL(context); return; } if (getRendererType() != null) { Renderer renderer = getRenderer(context); if (renderer != null) { renderer.encodeEnd(context, this); } // We've already logged for this component } popComponentFromEL(context); } // -------------------------------------------------- Event Listener Methods /** *

* 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: *

* *
     * public class FooEvent extends FacesEvent {
     *   ...
     *   protected boolean isAppropriateListener(FacesListener listener) {
     *     return (listener instanceof FooListener);
     *   }
     *   protected void processListener(FacesListener listener) {
     *     ((FooListener) listener).processFoo(this);
     *   }
     *   ...
     * }
     * 
     * public interface FooListener extends FacesListener {
     *   public void processFoo(FooEvent event);
     * }
     * 
     * public class FooComponent extends UIComponentBase {
     *   ...
     *   public void addFooListener(FooListener listener) {
     *     addFacesListener(listener);
     *   }
     *   public void removeFooListener(FooListener listener) {
     *     removeFacesListener(listener);
     *   }
     *   ...
     * }
     * 
* * @param listener The {@link FacesListener} to be registered * @throws NullPointerException if listener is null */ @Override protected void addFacesListener(FacesListener listener) { if (listener == null) { throw new NullPointerException(); } if (listeners == null) { listeners = new AttachedObjectListHolder<>(); } listeners.add(listener); } /** * @throws IllegalArgumentException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ @Override 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 results = new ArrayList<>(listeners.length); for (FacesListener listener : listeners) { if (((Class) clazz).isAssignableFrom(listener.getClass())) { results.add(listener); } } return results.toArray((FacesListener[]) Array.newInstance(clazz, results.size())); } /** *

* 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 listener is null */ @Override protected void removeFacesListener(FacesListener listener) { if (listener == null) { throw new NullPointerException(); } if (listeners != null) { listeners.remove(listener); } } /** * @throws IllegalStateException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ @Override public void queueEvent(FacesEvent event) { if (event == null) { throw new NullPointerException(); } UIComponent parent = getParent(); if (parent == null) { throw new IllegalStateException(); } parent.queueEvent(event); } /** *

* Install the listener instance referenced by argument componentListener as a * listener for events of type eventClass originating from this specific instance * of UIComponent. The default implementation creates an inner * {@link SystemEventListener} instance that wraps argument componentListener as * the listener argument. This inner class must call through to the argument * componentListener in its implementation of * {@link SystemEventListener#processEvent} and its implementation of * {@link SystemEventListener#isListenerForSource} must return true if the instance class of * this UIComponent is assignable from the argument to * isListenerForSource. *

* * @param eventClass the Class of event for which listener 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 facesEventClass are fired. * * @throws NullPointerException if any of the arguments are null. * * @since 2.1 */ @Override public void subscribeToEvent(Class eventClass, ComponentSystemEventListener componentListener) { if (isAnyNull(eventClass, componentListener)) { throw new NullPointerException(); } if (initialStateMarked()) { initialState = false; } if (listenersByEventClass == null) { listenersByEventClass = new HashMap<>(3, 1.0f); } SystemEventListener facesLifecycleListener = new ComponentSystemEventListenerAdapter(componentListener, this); List listenersForEventClass = listenersByEventClass.get(eventClass); if (listenersForEventClass == null) { listenersForEventClass = new ArrayList<>(3); listenersByEventClass.put(eventClass, listenersForEventClass); } if (!listenersForEventClass.contains(facesLifecycleListener)) { listenersForEventClass.add(facesLifecycleListener); } } /** *

* Remove the listener instance referenced by argument componentListener as a * listener for events of type eventClass originating from this specific instance * of UIComponent. When doing the comparison to determine if an existing listener * is equal to the argument componentListener (and thus must be removed), the * equals() method on the existing listener must be invoked, passing the * argument componentListener, rather than the other way around. *

* * @param eventClass the Class of event for which listener must be * removed. * @param componentListener the implementation of {@link ComponentSystemEventListener} whose * {@link ComponentSystemEventListener#processEvent} method must no longer be called * when events of type eventClass are fired. * * @throws NullPointerException if any of the arguments are null. * * @since 2.1 */ @Override public void unsubscribeFromEvent(Class eventClass, ComponentSystemEventListener componentListener) { if (isAnyNull(eventClass, componentListener)) { throw new NullPointerException(); } List listeners = getListenersForEventClass(eventClass); if (!isEmpty(listeners)) { for (Iterator i = listeners.iterator(); i.hasNext();) { ComponentSystemEventListener existingListener = ((ComponentSystemEventListenerAdapter) i.next()).getWrapped(); if (existingListener.equals(componentListener)) { i.remove(); break; } } } } /** *

* Return the SystemEventListener instances registered on this * UIComponent instance that are interested in events of type * eventClass. *

* * @param eventClass the Class of event for which the listeners must be returned. * * @throws NullPointerException if argument eventClass is null. * * @since 2.1 */ @Override public List getListenersForEventClass(Class eventClass) { if (eventClass == null) { throw new NullPointerException(); } if (listenersByEventClass != null) { return listenersByEventClass.get(eventClass); } return null; } // ------------------------------------------------ Lifecycle Phase Handlers /** * @throws NullPointerException {@inheritDoc} */ @Override 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); try { // 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} */ @Override 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); try { Application application = context.getApplication(); application.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); } application.publishEvent(context, PostValidateEvent.class, this); } finally { popComponentFromEL(context); } } /** * @throws NullPointerException {@inheritDoc} */ @Override 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); try { // Process all facets and children of this component Iterator kids = getFacetsAndChildren(); while (kids.hasNext()) { UIComponent kid = (UIComponent) kids.next(); kid.processUpdates(context); } } finally { popComponentFromEL(context); } } /** * @throws NullPointerException {@inheritDoc} */ @Override public Object processSaveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } if (isTransient()) { return null; } Object[] stateStruct = new Object[2]; Object[] childState = EMPTY_ARRAY; pushComponentToEL(context, null); try { // Process this component itself stateStruct[MY_STATE] = saveState(context); // Determine if we have any children to store int count = getChildCount() + getFacetCount(); if (count > 0) { // This arraylist will store state List stateList = new ArrayList<>(count); // If we have children, add them to the stateList collectChildState(context, stateList); // If we have facets, add them to the stateList collectFacetsState(context, stateList); // Finally, capture the stateList and replace the original, // EMPTY_OBJECT_ARRAY Object array childState = stateList.toArray(); } } finally { popComponentFromEL(context); } stateStruct[CHILD_STATE] = childState; return stateStruct; } /** * @throws NullPointerException {@inheritDoc} */ @Override public void processRestoreState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } pushComponentToEL(context, null); try { Object[] stateStruct = (Object[]) state; Object[] childState = (Object[]) stateStruct[CHILD_STATE]; // Process this component itself restoreState(context, stateStruct[MY_STATE]); // Process all the children of this component int i = restoreChildState(context, childState); // Process all of the facets of this component restoreFacetsState(context, childState, i); } finally { popComponentFromEL(context); } } // ------------------------------------------------------- Protected Methods @Override 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(); } @Override protected Renderer getRenderer(FacesContext context) { Renderer renderer = null; String rendererType = getRendererType(); if (rendererType != null) { renderer = context.getRenderKit().getRenderer(getFamily(), rendererType); if (renderer == null && LOGGER.isLoggable(FINE)) { LOGGER.fine("Can't get Renderer for type " + rendererType); } } else { if (LOGGER.isLoggable(FINE)) { String id = getId(); LOGGER.fine("No renderer-type for component " + id != null ? id : getClass().getName()); } } return renderer; } // ---------------------------------------------- PartialStateHolder Methods /** *

* For each of the attached objects on this instance that implement {@link PartialStateHolder}, * call {@link PartialStateHolder#markInitialState} on the attached object. *

* * @since 2.0 */ @Override public void markInitialState() { super.markInitialState(); if (listeners != null) { listeners.markInitialState(); } if (listenersByEventClass != null) { for (List listener : listenersByEventClass.values()) { if (listener instanceof PartialStateHolder) { ((PartialStateHolder) listener).markInitialState(); } } } if (behaviors != null) { for (Entry> entry : behaviors.entrySet()) { for (ClientBehavior behavior : entry.getValue()) { if (behavior instanceof PartialStateHolder) { ((PartialStateHolder) behavior).markInitialState(); } } } } } /** *

* For each of the attached objects on this instance that implement {@link PartialStateHolder}, * call {@link PartialStateHolder#clearInitialState} on the attached object. *

* * @since 2.0 */ @Override public void clearInitialState() { super.clearInitialState(); if (listeners != null) { listeners.clearInitialState(); } if (listenersByEventClass != null) { for (List listener : listenersByEventClass.values()) { if (listener instanceof PartialStateHolder) { ((PartialStateHolder) listener).clearInitialState(); } } } if (behaviors != null) { for (Entry> entry : behaviors.entrySet()) { for (ClientBehavior behavior : entry.getValue()) { if (behavior instanceof PartialStateHolder) { ((PartialStateHolder) behavior).clearInitialState(); } } } } } @Override public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } Object[] values = null; 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 (isAllNull(savedFacesListeners, savedSysEventListeners, savedBehaviors, savedBindings, savedHelper)) { return null; } 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; } } @Override 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<>(); } listeners.restoreState(context, values[0]); } if (values[1] != null) { Map, List> restoredListeners = restoreSystemEventListeners(context, values[1]); if (listenersByEventClass != null) { listenersByEventClass.putAll(restoredListeners); } else { listenersByEventClass = restoredListeners; } } 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]; } } } @Override public boolean isTransient() { return transientFlag; } @Override public void setTransient(boolean transientFlag) { this.transientFlag = transientFlag; } // -------------------------------------- Helper methods for state saving // --------- methods used by UIComponents to save their attached Objects. /** *

* 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. *

* *

* This method supports saving attached objects of the following type: Objects, * null values, and Collections of these objects. If any contained objects * are not Collections 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. *

* * @param context the {@link FacesContext} for this request. * @param attachedObject the object, which may be a List instance, or an Object. * The attachedObject (or the elements that comprise * attachedObject may implement {@link StateHolder}. * * @return The state object to be saved. * @throws NullPointerException if the context argument is null. */ public static Object saveAttachedState(FacesContext context, Object attachedObject) { if (context == null) { throw new NullPointerException(); } if (attachedObject == null) { 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 resultList = null; for (Object item : attachedCollection) { if (item != null) { if (item instanceof StateHolder && ((StateHolder) item).isTransient()) { continue; } if (resultList == null) { resultList = new ArrayList<>(attachedCollection.size() + 1); resultList.add(new StateHolderSaver(context, mapOrCollectionClass)); } resultList.add(new StateHolderSaver(context, item)); } } result = resultList; } else if (newWillSucceed && attachedObject instanceof Map) { Map attachedMap = (Map) attachedObject; List resultList = null; Object key, value; for (Map.Entry 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<>(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; } /** *

* 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}. *

* *

* This method supports restoring all attached objects types supported by * {@link #saveAttachedState}. *

* * @param context the {@link FacesContext} for this request * @param stateObj the opaque object returned from {@link #saveAttachedState} * * @return the object restored from stateObj. * * @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 stateList = (List) stateObj; StateHolderSaver collectionSaver = stateList.get(0); Class mapOrCollection = (Class) collectionSaver.restore(context); if (Collection.class.isAssignableFrom(mapOrCollection)) { Collection retCollection = null; try { retCollection = (Collection) mapOrCollection.newInstance(); } catch (InstantiationException | IllegalAccessException 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 retMap = null; try { retMap = (Map) mapOrCollection.newInstance(); } catch (InstantiationException | IllegalAccessException 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 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 bindings = new HashMap<>(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, List> 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, List> restoreSystemEventListeners(FacesContext ctx, Object state) { if (state == null) { return null; } Object[][] listeners = (Object[][]) state; Map, List> m = new HashMap<>(listeners.length, 1.0f); for (int i = 0, len = listeners.length; i < len; i++) { Object[] source = listeners[i]; m.put((Class) source[0], (List) restoreAttachedState(ctx, source[1])); } return m; } Map getDescriptorMap() { return propertyDescriptorMap; } 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; /** *

* This is a default implementation of * {@link javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior}. * UIComponent 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}. *

* * @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 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> 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> modifiableMap = new HashMap<>(5, 1.0f); behaviors = new BehaviorsMap(modifiableMap); } List eventBehaviours = behaviors.get(eventName); if (null == eventBehaviours) { // Again using small initial capacity - we typically // only have 1 Behavior per event type. eventBehaviours = new ArrayList<>(3); behaviors.getModifiableMap().put(eventName, eventBehaviours); } eventBehaviours.add(behavior); } } /** *

* This is a default implementation of * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}. * UIComponent 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 Collection of the client event names * that the component supports. *

* * @return the collection of event names. * @since 2.0 */ public Collection 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; } /** *

* This is a default implementation of * {@link javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors}. * UIComponent 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}. *

* * @return behaviors associated with this component. * @since 2.0 */ public Map> getClientBehaviors() { if (null == behaviors) { return Collections.emptyMap(); } return behaviors; } /** *

* This is a default implementation of * {@link javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName}. * UIComponent 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}. *

* * @return the default event name. */ 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 Object 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 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 Map 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> modifiableMap = new HashMap<>(names.length, 1.0f); for (int i = 0; i < attachedBehaviors.length; i++) { Object[] attachedEventBehaviors = (Object[]) attachedBehaviors[i]; ArrayList eventBehaviors = new ArrayList<>(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. if (behaviors != null) { List 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 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 clist = new ArrayList<>(component.getChildren()); for (UIComponent c : clist) { publishAfterViewEvents(context, application, c); } } if (component.getFacetCount() > 0) { Collection clist = new ArrayList<>(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 children = component.getChildren(); for (UIComponent c : children) { disconnectFromView(context, application, c); } } if (component.getFacetCount() > 0) { Map 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 EMPTY_ITERATOR = new Iterator() { @Override public void remove() { throw new UnsupportedOperationException(); } @Override public UIComponent next() { throw new NoSuchElementException("Empty Iterator"); } @Override 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, 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 attributes; private transient Map pdMap; private transient ConcurrentMap readMap; private transient UIComponent component; private static final long serialVersionUID = -6773035086539772945L; // -------------------------------------------------------- Constructors private AttributesMap(UIComponent component) { this.component = component; this.pdMap = ((UIComponentBase) component).getDescriptorMap(); } @Override 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 attributes = (Map) component.getStateHelper().get(PropertyKeys.attributes); if (attributes != null) { return attributes.containsKey(key); } else { return (false); } } else { return (false); } } @Override 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 attributes = (Map) component.getStateHelper().get(PropertyKeys.attributes); if (null == result) { PropertyDescriptor pd = getPropertyDescriptor(key); if (pd != null) { try { if (null == readMap) { readMap = new ConcurrentHashMap<>(); } Method readMethod = readMap.get(key); if (null == readMethod) { readMethod = pd.getReadMethod(); Method putResult = readMap.putIfAbsent(key, readMethod); if (null != putResult) { readMethod = putResult; } } 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; } @Override 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 sProperties = (List) 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); } } @Override public void putAll(Map map) { if (map == null) { throw new NullPointerException(); } for (Map.Entry entry : map.entrySet()) { this.put(entry.getKey(), entry.getValue()); } } @Override 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 attributes = getAttributes(); if (attributes != null) { component.getStateHelper().remove(UIComponent.PropertyKeysPrivate.attributesThatAreSet, key); return (component.getStateHelper().remove(PropertyKeys.attributes, key)); } else { return null; } } } @Override public int size() { Map attributes = getAttributes(); return (attributes != null ? attributes.size() : 0); } @Override public boolean isEmpty() { Map attributes = getAttributes(); return (attributes == null || attributes.isEmpty()); } @Override public boolean containsValue(java.lang.Object value) { Map attributes = getAttributes(); return (attributes != null && attributes.containsValue(value)); } @Override public void clear() { component.getStateHelper().remove(PropertyKeys.attributes); component.getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet); } @Override public Set keySet() { Map attributes = getAttributes(); if (attributes != null) return Collections.unmodifiableSet(attributes.keySet()); return Collections.emptySet(); } @Override public Collection values() { Map attributes = getAttributes(); if (attributes != null) return Collections.unmodifiableCollection(attributes.values()); return Collections.emptyList(); } @Override public Set> entrySet() { Map attributes = getAttributes(); if (attributes != null) return Collections.unmodifiableSet(attributes.entrySet()); return Collections.emptySet(); } @Override 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 | NullPointerException unused) { return false; } return true; } @Override public int hashCode() { int h = 0; for (Object o : entrySet()) { h += o.hashCode(); } return h; } private Map getAttributes() { return (Map) component.getStateHelper().get(PropertyKeys.attributes); } private Object putAttribute(String key, Object value) { return component.getStateHelper().put(PropertyKeys.attributes, key, value); } /** *

* Return the PropertyDescriptor for the specified property name for this * {@link UIComponent}'s implementation class, if any; otherwise, return null. *

* * @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 (InstantiationException | IllegalAccessException 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 { private UIComponent component; public ChildrenList(UIComponent component) { super(6); this.component = component; } @Override 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); } } @Override public boolean add(UIComponent element) { if (element == null) { throw new NullPointerException(); } else { eraseParent(element); boolean result = super.add(element); element.setParent(component); return result; } } @Override public boolean addAll(Collection collection) { Iterator elements = (new ArrayList(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); } @Override public boolean addAll(int index, Collection collection) { Iterator elements = (new ArrayList(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); } @Override 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(); } @Override public Iterator iterator() { return (new ChildrenListIterator(this)); } @Override public ListIterator listIterator() { return (new ChildrenListIterator(this)); } @Override public ListIterator listIterator(int index) { return (new ChildrenListIterator(this, index)); } @Override public UIComponent remove(int index) { UIComponent child = get(index); child.setParent(null); super.remove(index); return (child); } @Override public boolean remove(Object elementObj) { UIComponent element = (UIComponent) elementObj; if (element == null) { throw new NullPointerException(); } if (super.indexOf(element) != -1) { element.setParent(null); } if (super.remove(element)) { return (true); } else { return (false); } } @Override public boolean removeAll(Collection collection) { boolean result = false; for (Object elements : collection) { if (remove(elements)) { result = true; } } return (result); } @Override 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); } @Override 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 { 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 @Override public boolean hasNext() { return (index < list.size()); } @Override public UIComponent next() { try { UIComponent o = list.get(index); last = index++; return (o); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(String.valueOf(index)); } } @Override public void remove() { if (last == -1) { throw new IllegalStateException(); } list.remove(last); if (last < index) { index--; } last = -1; } // ListIterator methods @Override public void add(UIComponent o) { last = -1; list.add(index++, o); } @Override public boolean hasPrevious() { return (index > 1); } @Override public int nextIndex() { return (index); } @Override 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(); } } @Override public int previousIndex() { return (index - 1); } @Override 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 { private Iterator 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; } } @Override public boolean hasNext() { this.update(); return this.iterator.hasNext(); } @Override public UIComponent next() { this.update(); return this.iterator.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } } // Private implementation of Map that supports the functionality // required by UIComponent.getFacets() private static class FacetsMap extends HashMap { private UIComponent component; public FacetsMap(UIComponent component) { super(3, 1.0f); this.component = component; } @Override public void clear() { Iterator keys = keySet().iterator(); while (keys.hasNext()) { keys.next(); keys.remove(); } super.clear(); } @Override public Set> entrySet() { return (new FacetsMapEntrySet(this)); } @Override public Set keySet() { return (new FacetsMapKeySet(this)); } @Override 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); } @Override public void putAll(Map map) { if (map == null) { throw new NullPointerException(); } for (Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public UIComponent remove(Object key) { UIComponent previous = get(key); if (previous != null) { previous.setParent(null); } super.remove(key); return (previous); } @Override public Collection values() { return (new FacetsMapValues(this)); } Iterator keySetIterator() { return ((new ArrayList<>(super.keySet())).iterator()); } } // Private implementation of Set for FacetsMap.getEntrySet() private static class FacetsMapEntrySet extends AbstractSet> { public FacetsMapEntrySet(FacetsMap map) { this.map = map; } private FacetsMap map = null; @Override public boolean add(Map.Entry o) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection> c) { throw new UnsupportedOperationException(); } @Override public void clear() { map.clear(); } @Override 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))); } } @Override public boolean isEmpty() { return (map.isEmpty()); } @Override public Iterator> iterator() { return (new FacetsMapEntrySetIterator(map)); } @Override 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); } } @Override public boolean removeAll(Collection c) { boolean result = false; for (Object element : c) { if (remove(element)) { result = true; } } return (result); } @Override 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); } @Override public int size() { return (map.size()); } } // Private implementation of Map.Entry for FacetsMapEntrySet private static class FacetsMapEntrySetEntry implements Map.Entry { public FacetsMapEntrySetEntry(FacetsMap map, String key) { this.map = map; this.key = key; } private FacetsMap map; private String key; @Override 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); } @Override public String getKey() { return (key); } @Override public UIComponent getValue() { return (map.get(key)); } @Override public int hashCode() { Object value = map.get(key); return (((key == null) ? 0 : key.hashCode()) ^ ((value == null) ? 0 : value.hashCode())); } @Override 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> { public FacetsMapEntrySetIterator(FacetsMap map) { this.map = map; this.iterator = map.keySetIterator(); } private FacetsMap map = null; private Iterator iterator = null; private Map.Entry last = null; @Override public boolean hasNext() { return (iterator.hasNext()); } @Override public Map.Entry next() { last = new FacetsMapEntrySetEntry(map, iterator.next()); return (last); } @Override 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 { public FacetsMapKeySet(FacetsMap map) { this.map = map; } private FacetsMap map = null; public boolean add(String o) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { map.clear(); } @Override public boolean contains(Object o) { return (map.containsKey(o)); } @Override public boolean containsAll(Collection c) { for (Object item : c) { if (!map.containsKey(item)) { return (false); } } return (true); } @Override public boolean isEmpty() { return (map.isEmpty()); } @Override public Iterator iterator() { return (new FacetsMapKeySetIterator(map)); } @Override public boolean remove(Object o) { if (map.containsKey(o)) { map.remove(o); return (true); } else { return (false); } } @Override public boolean removeAll(Collection c) { boolean result = false; for (Object item : c) { if (map.containsKey(item)) { map.remove(item); result = true; } } return (result); } @Override 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); } @Override public int size() { return (map.size()); } } // Private implementation of Set for FacetsMap.getKeySet().iterator() private static class FacetsMapKeySetIterator implements Iterator { public FacetsMapKeySetIterator(FacetsMap map) { this.map = map; this.iterator = map.keySetIterator(); } private FacetsMap map = null; private Iterator iterator = null; private String last = null; @Override public boolean hasNext() { return (iterator.hasNext()); } @Override public String next() { last = iterator.next(); return (last); } @Override 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 { public FacetsMapValues(FacetsMap map) { this.map = map; } private FacetsMap map; @Override public boolean add(UIComponent o) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { map.clear(); } @Override public boolean isEmpty() { return (map.isEmpty()); } @Override public Iterator iterator() { return (new FacetsMapValuesIterator(map)); } @Override public int size() { return (map.size()); } } // Private implementation of Iterator for FacetsMap.values().iterator() private static class FacetsMapValuesIterator implements Iterator { public FacetsMapValuesIterator(FacetsMap map) { this.map = map; this.iterator = map.keySetIterator(); } private FacetsMap map = null; private Iterator iterator = null; private Object last = null; @Override public boolean hasNext() { return (iterator.hasNext()); } @Override public UIComponent next() { last = iterator.next(); return (map.get(last)); } @Override 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> { private Map> unmodifiableMap; private Map> modifiableMap; private BehaviorsMap(Map> modifiableMap) { this.modifiableMap = modifiableMap; this.unmodifiableMap = Collections.unmodifiableMap(modifiableMap); } @Override public Set>> entrySet() { return unmodifiableMap.entrySet(); } private Map> getModifiableMap() { return modifiableMap; } } private static class PassThroughAttributesMap extends ConcurrentHashMap implements Serializable { private static final long serialVersionUID = 4230540513272170861L; @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(); } } } @SuppressWarnings("unchecked") private void populateDescriptorsMapIfNecessary() { FacesContext facesContext = FacesContext.getCurrentInstance(); Class clazz = getClass(); /* * If we can find a valid FacesContext we are going to use it to get access to the property * descriptor map. */ if (facesContext != null && facesContext.getExternalContext() != null && facesContext.getExternalContext().getApplicationMap() != null) { Map applicationMap = facesContext.getExternalContext().getApplicationMap(); if (!applicationMap.containsKey("com.sun.faces.compnent.COMPONENT_DESCRIPTORS_MAP")) { applicationMap.put("com.sun.faces.compnent.COMPONENT_DESCRIPTORS_MAP", new ConcurrentHashMap<>()); } descriptors = (Map, Map>) applicationMap.get("com.sun.faces.compnent.COMPONENT_DESCRIPTORS_MAP"); propertyDescriptorMap = descriptors.get(clazz); } if (propertyDescriptorMap == null) { // We did not find the property descriptor map so we are now going to load it. PropertyDescriptor propertyDescriptors[] = getPropertyDescriptors(); if (propertyDescriptors != null) { propertyDescriptorMap = new HashMap<>(propertyDescriptors.length, 1.0f); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { propertyDescriptorMap.put(propertyDescriptor.getName(), propertyDescriptor); } if (LOGGER.isLoggable(FINE)) { LOGGER.log(FINE, "fine.component.populating_descriptor_map", new Object[] { clazz, currentThread().getName() }); } if (descriptors != null && !descriptors.containsKey(clazz)) { descriptors.put(clazz, propertyDescriptorMap); } } } } /** *

* Return an array of PropertyDescriptors for this {@link UIComponent}'s * implementation class. If no descriptors can be identified, a zero-length array will be * returned. *

* * @throws FacesException if an introspection exception occurs */ private PropertyDescriptor[] getPropertyDescriptors() { try { return getBeanInfo(getClass()).getPropertyDescriptors(); } catch (IntrospectionException e) { throw new FacesException(e); } } private String addParentId(FacesContext context, String parentId, String childId) { return new StringBuilder(parentId.length() + 1 + childId.length()) .append(parentId) .append(UINamingContainer.getSeparatorChar(context)) .append(childId) .toString(); } private String getParentId(FacesContext context, UIComponent parent) { if (parent == null) { return null; } return parent.getContainerClientId(context); } private String generateId(FacesContext context, UIComponent namingContainerAncestor) { if (namingContainerAncestor instanceof UniqueIdVendor) { return ((UniqueIdVendor) namingContainerAncestor).createUniqueId(context, null); } return context.getViewRoot().createUniqueId(); } private UIComponent getNamingContainerAncestor() { UIComponent namingContainer = getParent(); while (namingContainer != null) { if (namingContainer instanceof NamingContainer) { return namingContainer; } namingContainer = namingContainer.getParent(); } return null; } /** *

* 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 parent property will always be * null when this method returns. *

* * @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 = 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"); } /** *

* Throw IllegalArgumentException if the specified component identifier is * non-null and not syntactically valid. *

* * @param id The component identifier to test */ private static void validateId(String id) { if (id == null) { return; } int idLength = id.length(); if (idLength < 1) { throw new IllegalArgumentException("Empty id attribute is not allowed"); } for (int i = 0; i < idLength; i++) { char c = id.charAt(i); if (i == 0) { if (!isLetter(c) && c != '_') { throw new IllegalArgumentException(id); } } else { if (!isLetter(c) && !isDigit(c) && c != '-' && c != '_') { throw new IllegalArgumentException(id); } } } } private UIComponent findBaseComponent(String expression, final char sepChar) { // Identify the base component from which we will perform our search UIComponent base = this; if (expression.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 expression = expression.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(); } } return base; } private UIComponent evaluateSearchExpression(UIComponent base, String expression, final String SEPARATOR_STRING) { UIComponent result = null; String[] segments = expression.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 result; } /** *

* Return the {@link UIComponent} (if any) with the specified id, searching * recursively starting at the specified base, 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. *

* * @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 component = null; for (Iterator i = base.getFacetsAndChildren(); i.hasNext();) { UIComponent kid = (UIComponent) i.next(); if (!(kid instanceof NamingContainer)) { if (checkId && id.equals(kid.getId())) { component = kid; break; } component = findComponent(kid, id, true); if (component != null) { break; } } else if (id.equals(kid.getId())) { component = kid; break; } } return component; } private List collectChildState(FacesContext context, List stateList) { if (getChildCount() > 0) { Iterator kids = getChildren().iterator(); while (kids.hasNext()) { UIComponent kid = (UIComponent) kids.next(); if (!kid.isTransient()) { stateList.add(kid.processSaveState(context)); } } } return stateList; } private List collectFacetsState(FacesContext context, List stateList) { if (getFacetCount() > 0) { Iterator> myFacets = getFacets().entrySet().iterator(); while (myFacets.hasNext()) { Map.Entry entry = myFacets.next(); UIComponent facet = (UIComponent) entry.getValue(); if (!facet.isTransient()) { Object facetState = facet.processSaveState(context); Object[] facetSaveState = new Object[2]; facetSaveState[0] = entry.getKey(); facetSaveState[1] = facetState; stateList.add(facetSaveState); } } } return stateList; } private int restoreChildState(FacesContext context, Object[] childState) { int i = 0; // Process all the children of this component if (getChildCount() > 0) { for (UIComponent kid : getChildren()) { if (kid.isTransient()) { continue; } Object currentState = childState[i++]; if (currentState == null) { continue; } kid.processRestoreState(context, currentState); } } return i; } private void restoreFacetsState(FacesContext context, Object[] childState, int i) { if (getFacetCount() > 0) { int j = 0; int facetsSize = getFacets().size(); while (j < facetsSize) { Object[] facetSaveState = (Object[]) childState[i++]; if (facetSaveState != null) { String facetName = (String) facetSaveState[0]; Object facetState = facetSaveState[1]; UIComponent facet = getFacets().get(facetName); facet.processRestoreState(context, facetState); } ++j; } } } // ------------------------------------------- Deprecated code /** * {@inheritDoc} * * @throws NullPointerException {@inheritDoc} * @deprecated This has been replaced by {@link #getValueExpression}. */ @Override 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}. */ @Override 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); } } }