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

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

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

package javax.faces.component;


import java.io.IOException;
import java.io.InputStream;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.FacesWrapper;
import javax.faces.application.Resource;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitHint;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PostRestoreStateEvent;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ComponentSystemEventListener;
import javax.faces.event.FacesEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.event.FacesListener;
import javax.faces.event.SystemEventListenerHolder;
import javax.faces.render.Renderer;

/**
 * 

UIComponent is * the base class for all user interface components in JavaServer Faces. * The set of {@link UIComponent} instances associated with a particular request * and response are organized into a component tree under a {@link * UIViewRoot} that represents the entire content of the request or * response.

* *

For the convenience of component developers, * {@link UIComponentBase} provides the default * behavior that is specified for a {@link UIComponent}, and is the base class * for all of the concrete {@link UIComponent} "base" implementations. * Component writers are encouraged to subclass * {@link UIComponentBase}, instead of directly * implementing this abstract class, to reduce the impact of any future changes * to the method signatures.

* *

If the {@link * javax.faces.event.ListenerFor} annotation is attached to the class * definition of a Component, that class must also * implement {@link javax.faces.event.ComponentSystemEventListener}. *

*/ public abstract class UIComponent implements PartialStateHolder, TransientStateHolder, SystemEventListenerHolder, ComponentSystemEventListener { private static Logger LOGGER = Logger.getLogger("javax.faces.component", "javax.faces.LogStrings"); /** *

The ServletContext init * parameter consulted by * the UIComponent to tell whether or not the * {@link #CURRENT_COMPONENT} and {@link #CURRENT_COMPOSITE_COMPONENT} * attribute keys should be honored as specified.

* *

If this parameter is not specified, or is set to false, the contract * specified by the {@link #CURRENT_COMPONENT} and * {@link #CURRENT_COMPOSITE_COMPONENT} method is not honored. If this * parameter is set to true, the contract is honored.

*/ public static final String HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME = "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES"; /** *

The key to which the * UIComponent currently being processed will be * associated with within the {@link FacesContext} attributes * map. The use of this constant is * deprecated. Please see {@link * #HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} to enable its * use.

* * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 * * @deprecated */ public static final String CURRENT_COMPONENT = "javax.faces.component.CURRENT_COMPONENT"; /** *

The key to which the * composite UIComponent currently being * processed will be associated with within the {@link FacesContext} * attributes map. The use of this * constant is deprecated. Please see {@link * #HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} to enable its * use.

* * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 * * @deprecated */ public static final String CURRENT_COMPOSITE_COMPONENT = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT"; /** *

The value of this constant is used as the key in the * component attribute map, the value for which is a * java.beans.BeanInfo implementation describing the composite * component. This BeanInfo is known as the * composite component BeanInfo.

* * @since 2.0 */ public static final String BEANINFO_KEY = "javax.faces.component.BEANINFO_KEY"; /** *

The value of this constant is used as the key * in the composite component BeanDescriptor for the * Map<PropertyDescriptor> that contains meta-information * for the declared facets for this composite component. * This map must contain an entry under the key {@link #COMPOSITE_FACET_NAME}, even * if no facets were explicitly declared. See {@link #COMPOSITE_FACET_NAME}.

* * @since 2.0 */ public static final String FACETS_KEY = "javax.faces.component.FACETS_KEY"; /** *

The value of this constant is used as the key * in the component attributes Map for the * {@link javax.faces.view.Location} in the view at which this component * instance resides.

* * @since 2.0 */ public static final String VIEW_LOCATION_KEY = "javax.faces.component.VIEW_LOCATION_KEY"; /** *

The value of this constant is used as the key * in the composite component BeanDescriptor for a * ValueExpression that evaluates to the * component-type of the composite component root * UIComponent for this composite component, if * one was declared by the composite component author.

* * @since 2.0 */ public static final String COMPOSITE_COMPONENT_TYPE_KEY = "javax.faces.component.COMPOSITE_COMPONENT_TYPE"; /** *

The value of this constant is used as the key * in the Map returned as described in {@link #FACETS_KEY} * for the * PropertyDescriptor describing the composite component facet. * The value of this constant is also used as the key in the Map * returned from {@link #getFacets}. In this case, it refers to the actual * facet that is the {@link javax.faces.component.UIPanel} that is the parent of the all * of the components in the <composite:implementation> * section of the composite component VDL file.

* * @since 2.0 */ public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME"; /** *

This constant enables one to quickly discover * the names of the declared composite component attributes that have been * given default values by the composite component author. The information * is exposed as a Collection<String> returned from the * getValue() method on the composite component * BeanDescriptor, when this constant is passed as the argument.

* * @since 2.1 */ public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES = "javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES"; enum PropertyKeysPrivate { attributesThatAreSet } /** * Properties that are tracked by state saving. */ enum PropertyKeys { rendered, attributes, bindings, rendererType, systemEventListeners, behaviors, passThroughAttributes } /** * List of attributes that have been set on the component (this * may be from setValueExpression, the attributes map, or setters * from the concrete HTML components. This allows * for faster rendering of attributes as this list is authoritative * on what has been set. */ List attributesThatAreSet; ComponentStateHelper stateHelper = null; UIComponent compositeParent; private transient Boolean isSetCurrentComponent; // -------------------------------------------------------------- Attributes /** *

Return a mutable * Map representing the attributes * (and properties, see below) associated wth this {@link UIComponent}, * keyed by attribute name (which must be a String). The returned * implementation must support all of the standard and optional * Map methods, plus support the following additional * requirements:

*
    *
  • The Map implementation must implement * the java.io.Serializable interface.
  • *
  • Any attempt to add a null key or value must * throw a NullPointerException.
  • *
  • Any attempt to add a key that is not a String must throw * a ClassCastException.
  • *
  • If the attribute name specified as a key matches a property * of this {@link UIComponent}'s implementation class, the following * methods will have special behavior: *
      *
    • containsKey - Return false.
    • *
    • get() - If the property is readable, call * the getter method and return the returned value (wrapping * primitive values in their corresponding wrapper classes); * otherwise throw IllegalArgumentException.
    • *
    • put() - If the property is writeable, call * the setter method to set the corresponding value (unwrapping * primitive values in their corresponding wrapper classes). * If the property is not writeable, or an attempt is made to * set a property of primitive type to null, * throw IllegalArgumentException.
    • *
    • remove - Throw * IllegalArgumentException.
    • *
  • *
* */ public abstract Map getAttributes(); /** *

This is a convenience method that * simply calls {@link #getPassThroughAttributes(boolean)}, passing {@code true} * as the argument. This method must never return {@code null}.

* * @since 2.2 */ public final Map getPassThroughAttributes(){ return getPassThroughAttributes(true); } /** *

This method has the same specification as * {@link #getPassThroughAttributes() } except that it is allowed to return * {@code null} if and only if the argument {@code create} is {@code false} * and no pass through attribute data structure exists for this instance. * The returned {@code Map} implementation must support all of the standard and optional * {@code Map} methods, plus support the following additional requirements. * The map must be stored in using {@link #getStateHelper}.

* *
* *

The {@code Map} implementation must implement {@code java.io.Serializable}.

* *

Any attempt to add a {@code null} key or value must throw a {@code NullPointerException}.

* *

Any attempt to add a key that is not a {@code String} must * throw an {@code IllegalArgumentException}.

* *

For backward compatibility with components that extend directly from * this class, a default implementation is provided that returns the empty * map.

* *
* * @param create if true, a new {@code Map} * instance will be created if it does not exist already. If * false, and there is no existing * Map instance, one will not be created and * null will be returned. * @return A {@code Map} instance, or {@code null}. * * @since 2.2 */ public Map getPassThroughAttributes(boolean create) { return Collections.emptyMap(); } // ---------------------------------------------------------------- Bindings /** * *

Call through to {@link #getValueExpression} and examine the * result. If the result is an instance of the wrapper class * mandated in {@link #setValueBinding}, extract the * ValueBinding instance and return it. Otherwise, * wrap the result in an implementation of * ValueBinding, and return it.

* * @param name Name of the attribute or property for which to retrieve a * {@link ValueBinding} * * @throws NullPointerException if name * is null * * @deprecated This has been replaced by {@link #getValueExpression}. */ public abstract ValueBinding getValueBinding(String name); /** *

Wrap the argument binding in an implementation of * {@link ValueExpression} and call through to {@link * #setValueExpression}.

* * @param name Name of the attribute or property for which to set a * {@link ValueBinding} * @param binding The {@link ValueBinding} to set, or null * to remove any currently set {@link ValueBinding} * * @throws IllegalArgumentException if name is one of * id or parent * @throws NullPointerException if name * is null * * @deprecated This has been replaced by {@link #setValueExpression}. */ public abstract void setValueBinding(String name, ValueBinding binding); // The set of ValueExpressions for this component, keyed by property // name This collection is lazily instantiated // The set of ValueExpressions for this component, keyed by property // name This collection is lazily instantiated @Deprecated protected Map bindings = null; /** *

Return the {@link ValueExpression} used to calculate the value for the * specified attribute or property name, if any.

* *

This method must be overridden and implemented for components that * comply with JSF 1.2 and later.

* * @since 1.2 * * @param name Name of the attribute or property for which to retrieve a * {@link ValueExpression} * * @throws NullPointerException if name * is null * */ public ValueExpression getValueExpression(String name) { if (name == null) { throw new NullPointerException(); } Map map = (Map) getStateHelper().get(UIComponentBase.PropertyKeys.bindings); return ((map != null) ? map.get(name) : null); } /** *

Set the {@link ValueExpression} used to calculate the value * for the specified attribute or property name, if any.

* *

The implementation must call {@link * ValueExpression#isLiteralText} on the argument * expression. If isLiteralText() returns * true, invoke {@link ValueExpression#getValue} on the * argument expression and pass the result as the value * parameter in a call to this.{@link * #getAttributes()}.put(name, value) where name * is the argument name. If an exception is thrown as * a result of calling {@link ValueExpression#getValue}, wrap it in * a {@link javax.faces.FacesException} and re-throw it. If * isLiteralText() returns false, simply * store the un-evaluated expression argument in the * collection of ValueExpressions under the key given * by the argument name.

* *

This method must be overridden and implemented for components that * comply with JSF 1.2 and later.

* * @since 1.2 * * @param name Name of the attribute or property for which to set a * {@link ValueExpression} * @param binding The {@link ValueExpression} to set, or null * to remove any currently set {@link ValueExpression} * * @throws IllegalArgumentException if name is one of * id or parent * @throws NullPointerException if name * is null * */ public void setValueExpression(String name, ValueExpression binding) { if (name == null) { throw new NullPointerException(); } else if ("id".equals(name) || "parent".equals(name)) { throw new IllegalArgumentException(); } if (binding != null) { if (!binding.isLiteralText()) { //if (bindings == null) { // //noinspection CollectionWithoutInitialCapacity // bindings = new HashMap(); //} // add this binding name to the 'attributesThatAreSet' list //List sProperties = (List) // getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet); List sProperties = (List) getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet); if (sProperties == null) { getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, name); } else if (!sProperties.contains(name)) { getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, name); } getStateHelper().put(UIComponentBase.PropertyKeys.bindings, name, binding); //bindings.put(name, binding); } else { ELContext context = FacesContext.getCurrentInstance().getELContext(); try { getAttributes().put(name, binding.getValue(context)); } catch (ELException ele) { throw new FacesException(ele); } } } else { //if (bindings != null) { // remove this binding name from the 'attributesThatAreSet' list // List sProperties = getAttributesThatAreSet(false); // if (sProperties != null) { // sProperties.remove(name); // } getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet, name); getStateHelper().remove(UIComponentBase.PropertyKeys.bindings, name); //bindings.remove(name); // if (bindings.isEmpty()) { // bindings = null; // } } // } } // -------------------------------------------------------------- Properties boolean initialState; /** *

An implementation of {@link * PartialStateHolder#markInitialState}, this method is called by * the runtime to indicate that the instance should start tracking * changes to its state.

* @since 2.0 */ public void markInitialState() { initialState = true; } /** *

An implementation of {@link * PartialStateHolder#initialStateMarked}, this method is called by * the runtime to test if the {@link * PartialStateHolder#markInitialState} method was called.

* @since 2.0 */ public boolean initialStateMarked() { return initialState; } /** *

An implementation of {@link * PartialStateHolder#clearInitialState}, this method is called by * the runtime to tell the instance to stop tracking state * changes.

* @since 2.0 */ public void clearInitialState() { initialState = false; } /** *

Return the {@link StateHelper} * instance used to help this component implement {@link * PartialStateHolder}.

* @since 2.0 */ protected StateHelper getStateHelper() { return getStateHelper(true); } /** *

Like {@link #getStateHelper()}, but * only create a state helper instance if the argument * creat is true.

* @param create if true, a new {@link StateHelper} * instance will be created if it does not exist already. If * false, and there is no existing * StateHelper instance, one will not be created and * null will be returned. * @since 2.0 */ protected StateHelper getStateHelper(boolean create) { if (create && stateHelper == null) { stateHelper = new ComponentStateHelper(this); } return stateHelper; } /** *

Return the {@link * TransientStateHelper} instance for this UIComponent * instance. The default implementation simply calls through to * {@link #getTransientStateHelper(boolean)} passing true * as the argument.

* * @since 2.1 */ public final TransientStateHelper getTransientStateHelper() { return getTransientStateHelper(true); } /** *

Return the {@link * TransientStateHelper} instance for this UIComponent * instance.

* * @param create if true create, if necessary, any * internal data structures. If false, do not create * any instances. In this case, it is possible for this method to * return null. * * @since 2.1 */ public TransientStateHelper getTransientStateHelper(boolean create) { if (create && stateHelper == null) { stateHelper = new ComponentStateHelper(this); } return stateHelper; } /** *

For components that need to support * the concept of transient state, this method will restore any * state saved on a prior call to {@link #saveTransientState}.

* * @since 2.1 */ public void restoreTransientState(FacesContext context, Object state) { boolean forceCreate = (state != null); TransientStateHelper helper = getTransientStateHelper(forceCreate); if (helper != null) { helper.restoreTransientState(context, state); } } /** *

For components that need to support * the concept of transient state, this method will save any state * that is known to be transient in nature.

* * @since 2.1 */ public Object saveTransientState(FacesContext context) { TransientStateHelper helper = getTransientStateHelper(false); return (helper == null) ? null : helper.saveTransientState(context); } private boolean isInView; /** *

Return true if this * component is within the view hierarchy otherwise * false * * @since 2.0 */ public boolean isInView() { return isInView; } /** *

Updates the status as to whether or * not this component is currently within the view hierarchy. * This method must never be called by developers; a {@link * UIComponent}'s internal implementation will call it as components * are added to or removed from a parent's child List * or facet Map.

* * @param isInView flag indicating whether or not this component is within * the view hierachy * * @since 2.0 */ public void setInView(boolean isInView) { this.isInView = isInView; } /** *

Enable EL to access the clientId * of a component. This is particularly useful in combination with the * component and cc implicit * objects. A default implementation is provided that simply calls * {@link FacesContext#getCurrentInstance} and then calls through to * {@link #getClientId(FacesContext)}.

* * @since 2.0 */ public String getClientId() { FacesContext context = FacesContext.getCurrentInstance(); return getClientId(context); } /** *

Return a client-side identifier for this component, generating * one if necessary. The associated {@link Renderer}, if any, * will be asked to convert the clientId to a form suitable for * transmission to the client.

* *

The return from this method must be the same value throughout * the lifetime of the instance, unless the id property * of the component is changed, or the component is placed in * a {@link NamingContainer} whose client ID changes (for example, * {@link UIData}). However, even in these cases, consecutive * calls to this method must always return the same value. The * implementation must follow these steps in determining the * clientId:

* *

Find the closest ancestor to this component in the view * hierarchy that implements NamingContainer. Call * getContainerClientId() on it and save the result as * the parentId local variable. Call {@link #getId} on * this component and save the result as the * myId local variable. If myId is * null, call * context.getViewRoot().createUniqueId() and assign * the result to myId. If parentId is * non-null, let myId equal parentId * + {@link UINamingContainer#getSeparatorChar} + myId. Call * {@link Renderer#convertClientId}, passing myId, and * return the result.

* * @param context The {@link FacesContext} for the current request * * @throws NullPointerException if context * is null */ public abstract String getClientId(FacesContext context); /** *

Allow components that implement {@link NamingContainer} to * selectively disable prepending their clientId to their * descendent's clientIds by breaking the prepending logic into a * seperately callable method. See {@link #getClientId} for usage.

* *

By default, this method will call through to {@link * #getClientId} and return the result. * * @since 1.2 * * @throws NullPointerException if context is * null */ public String getContainerClientId(FacesContext context) { if (context == null) { throw new NullPointerException(); } return this.getClientId(context); } /** *

Return the identifier of the component family to which this * component belongs. This identifier, in conjunction with the value * of the rendererType property, may be used to select * the appropriate {@link Renderer} for this component instance.

*/ public abstract String getFamily(); /** *

Return the component identifier of this {@link UIComponent}.

*/ public abstract String getId(); /** *

Set the component identifier of this {@link UIComponent} (if any). * Component identifiers must obey the following syntax restrictions:

*
    *
  • Must not be a zero-length String.
  • *
  • First character must be a letter or an underscore ('_').
  • *
  • Subsequent characters must be a letter, a digit, * an underscore ('_'), or a dash ('-').
  • *
  • *
* *

Component identifiers must also obey the following semantic * restrictions (note that this restriction is NOT * enforced by the setId() implementation):

*
    *
  • The specified identifier must be unique among all the components * (including facets) that are descendents of the nearest ancestor * {@link UIComponent} that is a {@link NamingContainer}, or within * the scope of the entire component tree if there is no such * ancestor that is a {@link NamingContainer}.
  • *
* * @param id The new component identifier, or null to indicate * that this {@link UIComponent} does not have a component identifier * * @throws IllegalArgumentException if id is not * syntactically valid */ public abstract void setId(String id); /** *

Return the parent {@link UIComponent} of this * UIComponent, if any. A component must allow child * components to be added to and removed from the list of children * of this component, even though the child component returns null * from getParent( ).

*/ public abstract UIComponent getParent(); /** *

Set the parent * UIComponent of this UIComponent. If * parent.isInView() returns true, calling * this method will first cause a {@link * javax.faces.event.PreRemoveFromViewEvent} to be published, for * this node, and then the children of this node. Then, once the * re-parenting has occurred, a {@link * javax.faces.event.PostAddToViewEvent} will be published as well, * first for this node, and then for the node's children, but only if any of the * following conditions are true.

*
*
    *
  • {@link * javax.faces.context.FacesContext#getCurrentPhaseId} returns * {@link javax.faces.event.PhaseId#RESTORE_VIEW} and partial * state saving is enabled.

  • *
  • {@link javax.faces.context.FacesContext#isPostback} * returns false and {@link * javax.faces.context.FacesContext#getCurrentPhaseId} returns * something other than {@link * javax.faces.event.PhaseId#RESTORE_VIEW}

  • *
*
*

This method must never * be called by developers; a {@link UIComponent}'s internal * implementation will call it as components are added to or removed * from a parent's child List or facet * Map.

* * @param parent The new parent, or null for the root node * of a component tree */ public abstract void setParent(UIComponent parent); /** *

Return true if this component (and its children) * should be rendered during the Render Response phase * of the request processing lifecycle.

*/ public abstract boolean isRendered(); /** *

Set the rendered property of this * {@link UIComponent}.

* * @param rendered If true render this component; * otherwise, do not render this component */ public abstract void setRendered(boolean rendered); /** *

Return the {@link Renderer} type for this {@link UIComponent} * (if any).

*/ public abstract String getRendererType(); /** *

Set the {@link Renderer} type for this {@link UIComponent}, * or null for components that render themselves.

* * @param rendererType Logical identifier of the type of * {@link Renderer} to use, or null for components * that render themselves */ public abstract void setRendererType(String rendererType); /** *

Return a flag indicating whether this component is responsible * for rendering its child components. The default implementation * in {@link UIComponentBase#getRendersChildren} tries to find the * renderer for this component. If it does, it calls {@link * Renderer#getRendersChildren} and returns the result. If it * doesn't, it 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 {@link * UIComponentBase#encodeChildren}.

*/ public abstract boolean getRendersChildren(); private Map resourceBundleMap = null; /** *

Return a * Map<String,String> of the * ResourceBundle for this component. A component may * have a ResourceBundle associated with it. This * bundle may contain localized properties relating to instances of * this component. The default implementation first looks for a * ResourceBundle with a base name equal to the fully * qualified class name of the current UIComponent this * and Locale equal to the Locale of the * current UIViewRoot. If no such bundle is found, and * the component is a composite component, let resourceName * be the resourceName of the {@link Resource} for this * composite component, replacing the file extension with * ".properties". Let libraryName be the * libraryName of the the {@link Resource} for this * composite component. Call {@link * javax.faces.application.ResourceHandler#createResource(java.lang.String,java.lang.String)}, * passing the derived resourceName and * libraryName. Note that this will automatically allow * for the localization of the ResourceBundle due to * the localization facility implemented in * createResource, which is specified in section * JSF.2.6.1.3 of the spec prose document. If the resultant {@link * Resource} exists and can be found, the InputStream * for the resource is used to create a ResourceBundle. * If either of the two previous steps for obtaining the * ResourceBundle for this component is successful, the * ResourceBundle is wrapped in a * Map<String,String> and returned. Otherwise * Collections.EMPTY_MAP is returned.

* * @since 2.0 */ public Map getResourceBundleMap() { if (null == resourceBundleMap) { // See if there is a ResourceBundle under the FQCN for this class String className = this.getClass().getName(); Locale currentLocale = null; FacesContext context = null; UIViewRoot root = null; ResourceBundle resourceBundle = null; // Step 1: look for a ResourceBundle under the FQCN of this instance if (null != (context = FacesContext.getCurrentInstance())) { if (null != (root = context.getViewRoot())) { currentLocale = root.getLocale(); } } if (null == currentLocale) { currentLocale = Locale.getDefault(); } try { resourceBundle = ResourceBundle.getBundle(className, currentLocale); } catch (MissingResourceException e) { // It is not an error if there is no ResourceBundle } // Step 2: if this is a composite component, look for a // ResourceBundle as a Resource if (null == resourceBundle) { if (this.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)) { Resource ccResource = (Resource) this.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY); if (null != ccResource) { if (null != (ccResource = findComponentResourceBundleLocaleMatch(context, ccResource.getResourceName(), ccResource.getLibraryName()))) { InputStream propertiesInputStream = null; try { propertiesInputStream = ccResource.getInputStream(); resourceBundle = new PropertyResourceBundle(propertiesInputStream); } catch (IOException ex) { Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex); } finally{ if(null != propertiesInputStream){ try{ propertiesInputStream.close(); } catch(IOException ioe){ if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, null, ioe); } } } } } } } } // Step 3: if the previous steps yielded a ResourceBundle, wrap it // with a Map if (null != resourceBundle) { final ResourceBundle bundle = resourceBundle; resourceBundleMap = new Map() { // this is an immutable Map public String toString() { StringBuffer sb = new StringBuffer(); Iterator> entries = this.entrySet().iterator(); Map.Entry cur; while (entries.hasNext()) { cur = entries.next(); sb.append(cur.getKey()).append(": ").append(cur.getValue()).append('\n'); } return sb.toString(); } // Do not need to implement for immutable Map public void clear() { throw new UnsupportedOperationException(); } public boolean containsKey(Object key) { boolean result = false; if (null != key) { result = (null != bundle.getObject(key.toString())); } return result; } public boolean containsValue(Object value) { Enumeration keys = bundle.getKeys(); boolean result = false; while (keys.hasMoreElements()) { Object curObj = bundle.getObject(keys.nextElement()); if ((curObj == value) || ((null != curObj) && curObj.equals(value))) { result = true; break; } } return result; } public Set> entrySet() { HashMap mappings = new HashMap(); Enumeration keys = bundle.getKeys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Object value = bundle.getObject(key); mappings.put(key, value); } return mappings.entrySet(); } @Override public boolean equals(Object obj) { return !((obj == null) || !(obj instanceof Map)) && entrySet().equals(((Map) obj).entrySet()); } public Object get(Object key) { if (null == key) { return null; } try { return bundle.getObject(key.toString()); } catch (MissingResourceException e) { return "???" + key + "???"; } } public int hashCode() { return bundle.hashCode(); } public boolean isEmpty() { Enumeration keys = bundle.getKeys(); return !keys.hasMoreElements(); } public Set keySet() { Set keySet = new HashSet(); Enumeration keys = bundle.getKeys(); while (keys.hasMoreElements()) { keySet.add(keys.nextElement()); } return keySet; } // Do not need to implement for immutable Map public Object put(Object k, Object v) { throw new UnsupportedOperationException(); } // Do not need to implement for immutable Map public void putAll(Map t) { throw new UnsupportedOperationException(); } // Do not need to implement for immutable Map public Object remove(Object k) { throw new UnsupportedOperationException(); } public int size() { int result = 0; Enumeration keys = bundle.getKeys(); while (keys.hasMoreElements()) { keys.nextElement(); result++; } return result; } public java.util.Collection values() { ArrayList result = new ArrayList(); Enumeration keys = bundle.getKeys(); while (keys.hasMoreElements()) { result.add( bundle.getObject(keys.nextElement())); } return result; } }; } if (null == resourceBundleMap) { resourceBundleMap = Collections.EMPTY_MAP; } } return resourceBundleMap; } // PENDING(rlubke): I'm sure there's a more efficient // way to handle this. private Resource findComponentResourceBundleLocaleMatch(FacesContext context, String resourceName, String libraryName) { Resource result = null; ResourceBundle resourceBundle = null; int i; if (-1 != (i = resourceName.lastIndexOf("."))) { resourceName = resourceName.substring(0, i) + ".properties"; if (null != context) { result = context.getApplication().getResourceHandler(). createResource(resourceName, libraryName); InputStream propertiesInputStream = null; try { propertiesInputStream = result.getInputStream(); resourceBundle = new PropertyResourceBundle(propertiesInputStream); } catch (IOException ex) { Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex); } finally{ if(null != propertiesInputStream){ try{ propertiesInputStream.close(); } catch(IOException ioe){ if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, null, ioe); } } } } } } result = (null != resourceBundle) ? result : null; return result; } // This is necessary for JSF components that extend from UIComponent // directly rather than extending from UIComponentBase. Such components // may need to have implementations provided for methods that originated // from a spec version more recent than the version with which the component // complies. Currently this private property is only consulted in the // getValueExpression() method. // private boolean isUIComponentBase; // private boolean isUIComponentBaseIsSet = false; // // private boolean isUIComponentBase() { // if (!isUIComponentBaseIsSet) { // isUIComponentBase = (this instanceof UIComponentBase); // } // // return isUIComponentBase; // } // ------------------------------------------------- Tree Management Methods /** *

Return a mutable * List representing the child {@link UIComponent}s * associated with this component. The returned implementation must * support all of the standard and optional List * methods, plus support the following additional requirements:

*
  • The List implementation must implement the * java.io.Serializable interface.
  • Any attempt * to add a null must throw a NullPointerException
  • *
  • Any attempt to add an object that does not implement {@link * UIComponent} must throw a ClassCastException.
  • Whenever a * new child component is added, the parent property of * the child must be set to this component instance. If the * parent property of the child was already non-null, * the child must first be removed from its previous parent (where * it may have been either a child or a facet).
  • Whenever an * existing child component is removed, the parent * property of the child must be set to null.
  • *
  • After the child component has * been added to the view, {@link * javax.faces.application.Application#publishEvent} must be * called, passing {@link * javax.faces.event.PostAddToViewEvent}.class as * the first argument and the newly added component as the * second argument if any the following cases are true.

    * *
      *
    • {@link * javax.faces.context.FacesContext#getCurrentPhaseId} returns * {@link javax.faces.event.PhaseId#RESTORE_VIEW} and partial * state saving is enabled.

    • *
    • {@link javax.faces.context.FacesContext#isPostback} * returns false and {@link * javax.faces.context.FacesContext#getCurrentPhaseId} returns * something other than {@link * javax.faces.event.PhaseId#RESTORE_VIEW}

    • *
    *
  • *
*/ public abstract List getChildren(); /** *

Return the number of child {@link UIComponent}s that are * associated with this {@link UIComponent}. If there are no * children, this method must return 0. The method must not cause * the creation of a child component list.

*/ public abstract int getChildCount(); /** *

Search for and * return the {@link UIComponent} with an id that * matches the specified search expression (if any), according to * the algorithm described below.

*

WARNING: The found * UIComponent instance, if any, is returned * without regard for its tree traversal context. * Retrieving an EL-bound attribute from the component is not safe. * EL expressions can contain implicit objects, such as * #{component}, which assume they are being evaluated * within the scope of a tree traversal context. Evaluating * expressions with these kinds of implicit objects outside of a * tree traversal context produces undefined results. See {@link * #invokeOnComponent} for a method that does * correctly account for the tree traversal context when operating * on the found UIComponent instance. {@link #invokeOnComponent} * is also useful to find components given a simple clientId. * *

Component identifiers are required to be unique within the scope of * the closest ancestor {@link NamingContainer} that encloses this * component (which might be this component itself). If there are no * {@link NamingContainer} components in the ancestry of this component, * the root component in the tree is treated as if it were a * {@link NamingContainer}, whether or not its class actually implements * the {@link NamingContainer} interface.

* *

A search expression consists of either an identifier * (which is matched exactly against the id property of * a {@link UIComponent}, or a series of such identifiers linked by * the {@link UINamingContainer#getSeparatorChar} character value. * The search algorithm should operates as follows, though alternate * alogrithms may be used as long as the end result is the same:

*
    *
  • Identify the {@link UIComponent} that will be the base for searching, * by stopping as soon as one of the following conditions is met: *
      *
    • If the search expression begins with the the separator character * (called an "absolute" search expression), * the base will be the root {@link UIComponent} of the component * tree. The leading separator character will be stripped off, * and the remainder of the search expression will be treated as * a "relative" search expression as described below.
    • *
    • Otherwise, if this {@link UIComponent} is a * {@link NamingContainer} it will serve as the basis.
    • *
    • Otherwise, search up the parents of this component. If * a {@link NamingContainer} is encountered, it will be the base. *
    • *
    • Otherwise (if no {@link NamingContainer} is encountered) * the root {@link UIComponent} will be the base.
    • *
  • *
  • The search expression (possibly modified in the previous step) is now * a "relative" search expression that will be used to locate the * component (if any) that has an id that matches, within * the scope of the base component. The match is performed as follows: *
      *
    • If the search expression is a simple identifier, this value is * compared to the id property, and then recursively * through the facets and children of the base {@link UIComponent} * (except that if a descendant {@link NamingContainer} is found, * its own facets and children are not searched).
    • *
    • If the search expression includes more than one identifier * separated by the separator character, the first identifier is * used to locate a {@link NamingContainer} by the rules in the * previous bullet point. Then, the findComponent() * method of this {@link NamingContainer} will be called, passing * the remainder of the search expression.
    • *
  • *
* * @param expr Search expression identifying the {@link UIComponent} * to be returned * * @return the found {@link UIComponent}, or null * if the component was not found. * * @throws IllegalArgumentException if an intermediate identifier * in a search expression identifies a {@link UIComponent} that is * not a {@link NamingContainer} * @throws NullPointerException if expr * is null */ public abstract UIComponent findComponent(String expr); /** *

Starting at this * component in the View hierarchy, search for a component with a * clientId equal to the argument clientId * and, if found, call the {@link * ContextCallback#invokeContextCallback} method on the argument * callback, passing the current {@link FacesContext} * and the found component as arguments. This method is similar to * {@link #findComponent} but it does not support the leading {@link * UINamingContainer#getSeparatorChar} syntax for searching from the * root of the View.

* *

The default implementation will first check if * this.getClientId() is equal to the argument * clientId. If so, first call {@link #pushComponentToEL}, * then call the {@link * ContextCallback#invokeContextCallback} method on the argument * callback, passing through the FacesContext argument * and passing this as the component argument. Then call {@link #popComponentFromEL}. * If an Exception is thrown by the callback, wrap it * in a {@link FacesException} and re-throw it. Otherwise, return * true.

* *

Otherwise, for each component returned by {@link * #getFacetsAndChildren}, call invokeOnComponent() * passing the arguments to this method, in order. The first time * invokeOnComponent() returns true, abort traversing * the rest of the Iterator and return * true.

* *

When calling {@link ContextCallback#invokeContextCallback} * the implementation of this method must guarantee that the state * of the component passed to the callback correctly reflects the * component's position in the View hierarchy with respect to any * state found in the argument clientId. For example, * an iterating component such as {@link UIData} will need to set * its row index to correctly reflect the argument * clientId before finding the appropriate child * component backed by the correct row. When the callback returns, * either normally or by throwing an Exception the * implementation of this method must restore the state of the view * to the way it was before invoking the callback.

* *

If none of the elements from {@link * #getFacetsAndChildren} returned true from * invokeOnComponent(), return false.

* *

Simple usage example to find a component by * clientId.

*

private UIComponent found = null;

private void doFind(FacesContext context, String clientId) {
  context.getViewRoot().invokeOnComponent(context, clientId,
      new ContextCallback() {
         public void invokeContextCallback(FacesContext context,
                                       UIComponent component) {
           found = component;
         }
      });
}
* 
* * * @since 1.2 * * @param context the {@link FacesContext} for the current request * * @param clientId the client identifier of the component to be passed * to the argument callback. * * @param callback an implementation of the Callback interface. * * @throws NullPointerException if any of the arguments are null * * @throws FacesException if the argument Callback throws an * Exception, it is wrapped in a FacesException and re-thrown. * * @return true if the a component with the given * clientId is found, the callback method was * successfully invoked passing that component as an argument, and * no Exception was thrown. Returns false if no * component with the given clientId is found. * */ public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException { if (null == context || null == clientId || null == callback) { throw new NullPointerException(); } boolean found = false; if (clientId.equals(this.getClientId(context))) { try { this.pushComponentToEL(context, this); callback.invokeContextCallback(context, this); return true; } catch (Exception e) { throw new FacesException(e); } finally { this.popComponentFromEL(context); } } else { Iterator itr = this.getFacetsAndChildren(); while (itr.hasNext() && !found) { found = itr.next().invokeOnComponent(context, clientId, callback); } } return found; } // ------------------------------------------------ Facet Management Methods /** *

Return a mutable Map representing the facet * {@link UIComponent}s associated with this {@link UIComponent}, * keyed by facet name (which must be a String). The returned * implementation must support all of the standard and optional * Map methods, plus support the following additional * requirements:

*
    *
  • The Map implementation must implement * the java.io.Serializable interface.
  • *
  • Any attempt to add a null key or value must * throw a NullPointerException.
  • *
  • Any attempt to add a key that is not a String must throw * a ClassCastException.
  • *
  • Any attempt to add a value that is not a {@link UIComponent} * must throw a ClassCastException.
  • *
  • Whenever a new facet {@link UIComponent} is added: *
      *
    • The parent property of the component must be set to * this component instance.
    • *
    • If the parent property of the component was already * non-null, the component must first be removed from its previous * parent (where it may have been either a child or a facet).
    • *
  • *
  • Whenever an existing facet {@link UIComponent} is removed: *
      *
    • The parent property of the facet must be * set to null.
    • *
  • *
*/ public abstract Map getFacets(); /** *

Return the number of facet {@link UIComponent}s that are * associated with this {@link UIComponent}. If there are no * facets, this method must return 0. The method must not cause * the creation of a facet component map.

* *

For backwards compatability with classes that extend UIComponent * directly, a default implementation is provided that simply calls * {@link #getFacets} and then calls the size() method on the * returned Map. A more optimized version of this method is * provided in {@link UIComponentBase#getFacetCount}. * * @since 1.2 */ public int getFacetCount() { return (getFacets().size()); } /** *

Convenience method to return the named facet, if it exists, or * null otherwise. If the requested facet does not * exist, the facets Map must not be created.

* * @param name Name of the desired facet */ public abstract UIComponent getFacet(String name); /** *

Return an Iterator over the facet followed by child * {@link UIComponent}s of this {@link UIComponent}. * Facets are returned in an undefined order, followed by * all the children in the order they are stored in the child list. If this * component has no facets or children, an empty Iterator * is returned.

* *

The returned Iterator must not support the * remove() operation.

*/ public abstract Iterator getFacetsAndChildren(); // -------------------------------------------- Lifecycle Processing Methods /** *

Broadcast the specified {@link FacesEvent} to all registered * event listeners who have expressed an interest in events of this * type. Listeners are called in the order in which they were * added.

*

If the event is an instance of * {@link javax.faces.event.BehaviorEvent} and the current * component is the source of the event * call {@link javax.faces.event.BehaviorEvent#getBehavior} to get the * {@link javax.faces.component.behavior.Behavior} for the event. * Call {@link * javax.faces.component.behavior.Behavior#broadcast(javax.faces.event.BehaviorEvent)} * on the Behavior instance.

* * @param event The {@link FacesEvent} to be broadcast * * @throws AbortProcessingException Signal the JavaServer Faces * implementation that no further processing on the current event * should be performed * @throws IllegalArgumentException if the implementation class * of this {@link FacesEvent} is not supported by this component * @throws NullPointerException if event is * null */ public abstract void broadcast(FacesEvent event) throws AbortProcessingException; /** *

Decode any new state of this {@link UIComponent} from the * request contained in the specified {@link FacesContext}, and store * this state as needed.

*

During decoding, events may be enqueued for later processing * (by event listeners who have registered an interest), by calling * queueEvent().

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public abstract void decode(FacesContext context); /** *

Perform a tree visit starting at * this node in the tree.

* *
* *

UIComponent.visitTree() implementations do not invoke the * {@link VisitCallback} directly, but instead call {@link * VisitContext#invokeVisitCallback} to invoke the callback. This * allows {@code VisitContext} implementations to provide optimized * tree traversals, for example by only calling the {@code * VisitCallback} for a subset of components.

* *

UIComponent.visitTree() implementations must call * UIComponent.pushComponentToEL() before performing the * visit and UIComponent.popComponentFromEL() after the * visit.

* * @param context the VisitContext for this visit * @param callback the VisitCallback instance * whose visit method will be called * for each node visited. * @return component implementations may return true * to indicate that the tree visit is complete (eg. all components * that need to be visited have been visited). This results in * the tree visit being short-circuited such that no more components * are visited. * *
* * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback() * * @since 2.0 */ public boolean visitTree(VisitContext context, VisitCallback callback) { // First check to see whether we are visitable. If not // short-circuit out of this subtree, though allow the // visit to proceed through to other subtrees. if (!isVisitable(context)) return false; // Push ourselves to EL before visiting FacesContext facesContext = context.getFacesContext(); pushComponentToEL(facesContext, null); try { // Visit ourselves. Note that we delegate to the // VisitContext to actually perform the visit. VisitResult result = context.invokeVisitCallback(this, callback); // If the visit is complete, short-circuit out and end the visit if (result == VisitResult.COMPLETE) return true; // Visit children if necessary if (result == VisitResult.ACCEPT) { Iterator kids = this.getFacetsAndChildren(); while(kids.hasNext()) { boolean done = kids.next().visitTree(context, callback); // If any kid visit returns true, we are done. if (done) return true; } } } finally { // Pop ourselves off the EL stack popComponentFromEL(facesContext); } // Return false to allow the visit to continue return false; } /** *

Return true if this * component should be visited, false otherwise. * Called by {@link UIComponent#visitTree UIComponent.visitTree()} * to determine whether this component satisfies the hints returned * by {@link javax.faces.component.visit.VisitContext#getHints}.

*
*

If this method returns false, the tree visited is * short-circuited such that neither the component nor any of its * descendents will be visited>

*

Custom {@code visitTree()} implementations may call this * method to determine whether the component is visitable before * performing any visit-related processing.

* *
* * @since 2.0 */ protected boolean isVisitable(VisitContext context) { // VisitHints currently defines two hints that affect // visitability: VIIST_RENDERED and VISIT_TRANSIENT. // Check for both of these and if set, verify that // we comply. Set hints = context.getHints(); if ((hints.contains(VisitHint.SKIP_UNRENDERED) && !this.isRendered()) || (hints.contains(VisitHint.SKIP_TRANSIENT) && this.isTransient())) { return false; } return true; } /** *

If our * rendered property is true, render the * beginning of the current state of this {@link UIComponent} to the * response contained in the specified {@link FacesContext}. * Call {@link #pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)}. * Call {@link javax.faces.application.Application#publishEvent}, passing * {@link javax.faces.event.PreRenderComponentEvent}.class as the * first argument and the component instance to be rendered as the * second argument.

*

If a {@link Renderer} is associated with this {@link * UIComponent}, the actual encoding will be delegated to * {@link Renderer#encodeBegin(FacesContext, UIComponent)}. *

* *

If our rendered property is * false, call {@link #pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)} * and return immediately.

* * @param context {@link FacesContext} for the response we are creating * * @throws IOException if an input/output error occurs while rendering * @throws NullPointerException if context * is null */ public abstract void encodeBegin(FacesContext context) throws IOException; /** *

If our rendered property is true, * render the child {@link UIComponent}s of this {@link UIComponent}. * This method will only be called * if the rendersChildren property is true.

* *

If a {@link Renderer} is associated with this {@link UIComponent}, * the actual encoding will be delegated to * {@link Renderer#encodeChildren(FacesContext, UIComponent)}. * If no {@link Renderer} is associated * with this {@link UIComponent}, iterate over each of the children of this * component and call * {@link #encodeAll(javax.faces.context.FacesContext)}.

* * @param context {@link FacesContext} for the response we are creating * * @throws IOException if an input/output error occurs while rendering * @throws NullPointerException if context * is null */ public abstract void encodeChildren(FacesContext context) throws IOException; /** *

If our * rendered property is true, render the * ending of the current state of this {@link UIComponent}.

* *

If a {@link Renderer} is associated with this {@link UIComponent}, * the actual encoding will be delegated to * {@link Renderer#encodeEnd(FacesContext, UIComponent)}.

* *

Call {@link * UIComponent#popComponentFromEL}. before returning regardless of the value * of the rendered property.

* * @param context {@link FacesContext} for the response we are creating * * @throws IOException if an input/output error occurs while rendering * @throws NullPointerException if context * is null */ public abstract void encodeEnd(FacesContext context) throws IOException; /** *

If this component * returns true from {@link #isRendered}, take the * following action.

* *

Render this component and all its children that return * true from isRendered(), regardless of * the value of the {@link #getRendersChildren} flag.

* @since 1.2 * * @throws IOException if an input/output error occurs while rendering * @throws NullPointerException if context * is null */ public void encodeAll(FacesContext context) throws IOException { if (context == null) { throw new NullPointerException(); } if (!isRendered()) { return; } encodeBegin(context); if (getRendersChildren()) { encodeChildren(context); } else if (this.getChildCount() > 0) { for (UIComponent kid : getChildren()) { kid.encodeAll(context); } } encodeEnd(context); } private static ArrayDeque _getComponentELStack(String keyName, Map contextAttributes) { ArrayDeque elStack = (ArrayDeque)contextAttributes.get(keyName); if (elStack == null) { elStack = new ArrayDeque(); contextAttributes.put(keyName, elStack); } return elStack; } // bugdb 18090503  /* * Respecting the fact that someone may have decorated FacesContextFactory * and thus skipped our saving of this init param, look for the init * param and return its value. The return is saved in a transient ivar * to provide performance while not perturbing state saving. */ private boolean isSetCurrentComponent(FacesContext context) { if (null != isSetCurrentComponent) { return isSetCurrentComponent; } Boolean bool = (Boolean) context.getAttributes().get(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME); if (null != bool) { isSetCurrentComponent = bool; } else { String val = context.getExternalContext().getInitParameter(UIComponent.HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME); isSetCurrentComponent = Boolean.valueOf(val); } return isSetCurrentComponent; } //private UIComponent previouslyPushed = null; //private UIComponent previouslyPushedCompositeComponent = null; //private boolean pushed; //private int depth; /** *

Push the current * UIComponent this to the {@link FacesContext} * attribute map using the key {@link #CURRENT_COMPONENT} saving the previous * UIComponent associated with {@link #CURRENT_COMPONENT} for a * subsequent call to {@link #popComponentFromEL}.

* * This method and popComponentFromEL() form the basis for * the contract that enables the EL Expression "#{component}" to * resolve to the "current" component that is being processed in the * lifecycle. The requirements for when pushComponentToEL() and * popComponentFromEL() must be called are specified as * needed in the javadoc for this class.

* *

After * pushComponentToEL() returns, a call to {@link * #getCurrentComponent} must return this * UIComponent instance until * popComponentFromEL() is called, after which point * the previous UIComponent instance will be returned * from getCurrentComponent()

* * @param context the {@link FacesContext} for the current request * @param component the component to push to the EL. If * component is null the UIComponent * instance that this call was invoked upon will be pushed to the EL. * * @throws NullPointerException if context is null * * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 */ public final void pushComponentToEL(FacesContext context, UIComponent component) { if (context == null) { throw new NullPointerException(); } if (null == component) { component = this; } Map contextAttributes = context.getAttributes(); ArrayDeque componentELStack = _getComponentELStack(_CURRENT_COMPONENT_STACK_KEY, contextAttributes); componentELStack.push(component); component._isPushedAsCurrentRefCount++; // we only do this because of the spec boolean setCurrentComponent = isSetCurrentComponent(context); if (setCurrentComponent) { contextAttributes.put(UIComponent.CURRENT_COMPONENT, component); } // if the pushed component is a composite component, we need to update that // stack as well if (UIComponent.isCompositeComponent(component)) { _getComponentELStack(_CURRENT_COMPOSITE_COMPONENT_STACK_KEY, contextAttributes).push(component); // we only do this because of the spec if (setCurrentComponent) { contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, component); } } } // track whether we have been pushed as current in order to handle mismatched pushes and // pops of EL context stack. We use a counter to handle cases where the same component // is pushed on multiple times private int _isPushedAsCurrentRefCount = 0; // key used to look up current component stack if FacesContext attributes private static final String _CURRENT_COMPONENT_STACK_KEY = "javax.faces.component.CURRENT_COMPONENT_STACK"; // key used to look up current composite component stack if FacesContext attributes private static final String _CURRENT_COMPOSITE_COMPONENT_STACK_KEY = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT_STACK"; /** *

Pop the current * UIComponent from the {@link FacesContext} attributes map * so that the previous UIComponent, if any, becomes the current * component.

* * @param context the {@link FacesContext} for the current request * * @throws NullPointerException if context is null * * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 */ public final void popComponentFromEL(FacesContext context) { if (context == null) { throw new NullPointerException(); } // detect cases where the stack has become unbalanced. Due to how UIComponentBase // implemented pushing and pooping of components from the ELContext, components that // overrode just one of encodeBegin or encodeEnd, or only called super in one case // will become unbalanced. Detect and correct for those cases here. // detect case where push was never called. In that case, pop should be a no-op if (_isPushedAsCurrentRefCount < 1) { return; } Map contextAttributes = context.getAttributes(); ArrayDeque componentELStack = _getComponentELStack(_CURRENT_COMPONENT_STACK_KEY, contextAttributes); // check for the other unbalanced case, a component was pushed but never popped. Keep // popping those components until we get to our component for (UIComponent topComponent = componentELStack.peek(); topComponent != this; topComponent = componentELStack.peek()) { topComponent.popComponentFromEL(context); } // pop ourselves off of the stack componentELStack.pop(); _isPushedAsCurrentRefCount--; boolean setCurrentComponent = isSetCurrentComponent(context); // update the current component with the new top of stack. We only do this because of the spec if (setCurrentComponent) { contextAttributes.put(UIComponent.CURRENT_COMPONENT, componentELStack.peek()); } // if we're a composite component, we also have to pop ourselves off of the // composite stack if (UIComponent.isCompositeComponent(this)) { ArrayDeque compositeELStack=_getComponentELStack(_CURRENT_COMPOSITE_COMPONENT_STACK_KEY, contextAttributes); if (!compositeELStack.isEmpty()) { compositeELStack.pop(); } // update the current composite component with the new top of stack. // We only do this because of the spec if (setCurrentComponent) { contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, compositeELStack.peek()); } } } // It is safe to cache this because components never go from being // composite to non-composite. private transient Boolean isCompositeComponent = null; /** *

Return true if * component is a composite component, otherwise * false.

* * @param component the {@link UIComponent} to test * * @throws NullPointerException if component is null * @since 2.0 */ public static boolean isCompositeComponent(UIComponent component) { if (component == null) { throw new NullPointerException(); } boolean result = false; if (null != component.isCompositeComponent) { result = component.isCompositeComponent.booleanValue(); } else { result = component.isCompositeComponent = (component.getAttributes().containsKey( Resource.COMPONENT_RESOURCE_KEY)); } return result; } /** *

* Finds the nearest composite component parent of the specified component. *

* * @param component the component from which to start the search from * * @return if component is null, return * null, otherwise search the component's parent hierachy * for the nearest parent composite component. If no parent composite * component is found, return null * * @since 2.0 */ public static UIComponent getCompositeComponentParent(UIComponent component) { if (component == null) { return null; } else { if (component.compositeParent != null) { return component.compositeParent; } UIComponent parent = component.getParent(); while (parent != null) { if (UIComponent.isCompositeComponent(parent)) { if (component.isInView()) { component.compositeParent = parent; } return parent; } parent = parent.getParent(); } return null; } } /** *

Return the UIComponent * instance that is currently processing. This is equivalent to * evaluating the EL expression "#{component}" and * doing a getValue operation on the resultant * ValueExpression.

* *

This method must return * null if there is no currently processing * UIComponent

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null * * @since 2.0 */ public static UIComponent getCurrentComponent(FacesContext context) { Map contextAttributes = context.getAttributes(); ArrayDeque componentELStack = _getComponentELStack(_CURRENT_COMPONENT_STACK_KEY, contextAttributes); return componentELStack.peek(); } /** *

Return the closest ancestor * component, relative to the component returned from {@link * #getCurrentComponent}, that is a composite component, or * null if no such component exists.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null * * @since 2.0 */ public static UIComponent getCurrentCompositeComponent(FacesContext context) { // return (UIComponent)context.getAttributes().get(UIComponent.CURRENT_COMPOSITE_COMPONENT); Map contextAttributes = context.getAttributes(); ArrayDeque compositeELStack = _getComponentELStack(_CURRENT_COMPOSITE_COMPONENT_STACK_KEY, contextAttributes); return compositeELStack.peek(); } // -------------------------------------------------- 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 { ... }
     *
     * 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 */ protected abstract void addFacesListener(FacesListener listener); /** *

Return an array of registered {@link FacesListener}s that are * instances of the specified class. If there are no such registered * listeners, a zero-length array is returned. The returned * array can be safely be cast to an array strongly typed to * an element type of clazz.

* * @param clazz Class that must be implemented by a {@link FacesListener} * for it to be returned * * @throws IllegalArgumentException if class is not, * and does not implement, {@link FacesListener} * @throws NullPointerException if clazz * is null */ protected abstract FacesListener[] getFacesListeners(Class clazz); /** *

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 */ protected abstract void removeFacesListener(FacesListener listener); /** *

Queue an event for broadcast at the end of the current request * processing lifecycle phase. The default implementation in * {@link UIComponentBase} must delegate this call to the * queueEvent() method of the parent {@link UIComponent}.

* * @param event {@link FacesEvent} to be queued * * @throws IllegalStateException if this component is not a * descendant of a {@link UIViewRoot} * @throws NullPointerException if event * is null */ public abstract void queueEvent(FacesEvent event); /** *

This implementation throws * UnsupportedOperationException and is provided * for the sole purpose of not breaking existing applications that extend * this class. {@link UIComponentBase} provides the implementation of * this method.

* * @since 2.1 */ public void subscribeToEvent(Class eventClass, ComponentSystemEventListener componentListener) { throw new UnsupportedOperationException(); } /** *

This implementation throws * UnsupportedOperationException and is provided * for the sole purpose of not breaking existing applications that extend * this class. {@link UIComponentBase} provides the implementation of * this method.

* * @since 2.1 */ public void unsubscribeFromEvent(Class eventClass, ComponentSystemEventListener componentListener) { throw new UnsupportedOperationException(); } /** *

This implementation throws * UnsupportedOperationException and is provided * for the sole purpose of not breaking existing applications that extend * this class. {@link UIComponentBase} provides the implementation of * this method.

* * @since 2.1 */ public List getListenersForEventClass(Class eventClass) { throw new UnsupportedOperationException(); } /** *

Starting with "this", return the closest * component in the ancestry that is a NamingContainer * or null if none can be found.

* * @since 2.0 */ public UIComponent getNamingContainer() { UIComponent namingContainer = this; while (namingContainer != null) { if (namingContainer instanceof NamingContainer) { return namingContainer; } namingContainer = namingContainer.getParent(); } return null; } // ------------------------------------------------ Lifecycle Phase Handlers /** *

Perform the * component tree processing required by the Restore View * phase of the request processing lifecycle for all facets of this * component, all children of this component, and this component * itself, as follows.

  • Call the restoreState() * method of this component.
  • * *
  • Call * {@link UIComponent#pushComponentToEL}.
  • *
  • Call the processRestoreState() method of all * facets and children of this {@link UIComponent} in the order * determined by a call to getFacetsAndChildren(). * After returning from the * processRestoreState() method on a child or facet, * call {@link UIComponent#popComponentFromEL}
  • *
* *

This method may not be called if the state saving method is * set to server.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public abstract void processRestoreState(FacesContext context, Object state); /** *

Perform the * component tree processing required by the Apply Request * Values phase of the request processing lifecycle for all * facets of this component, all children of this component, and * this component itself, as follows.

*
    *
  • If the rendered property of this {@link UIComponent} * is false, skip further processing.
  • *
  • Call {@link #pushComponentToEL}.
  • *
  • Call the processDecodes() method of all facets * and children of this {@link UIComponent}, in the order determined * by a call to getFacetsAndChildren().
  • *
  • Call the decode() method of this component.
  • *
  • Call {@link #popComponentFromEL} from inside of a * finally block, just before returning.
  • *
  • If a RuntimeException is thrown during * decode processing, call {@link FacesContext#renderResponse} * and re-throw the exception.
  • *
* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public abstract void processDecodes(FacesContext context); /** *

The default implementation performs * the following action. If the argument event is an * instance of {@link PostRestoreStateEvent}, call * this.{@link #getValueExpression} passing the literal * string “binding”, without the quotes, as the * argument. If the result is non-null, set the value * of the ValueExpression to be this.

*/ public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { if (event instanceof PostRestoreStateEvent) { assert(this == event.getComponent()); // if this component has a component value reference expression, // make sure to populate the ValueExpression for it. ValueExpression valueExpression; if (null != (valueExpression = this.getValueExpression("binding"))) { valueExpression.setValue(FacesContext.getCurrentInstance().getELContext(), this); } isCompositeComponent = null; } } /** *

Perform the component * tree processing required by the Process Validations * phase of the request processing lifecycle for all facets of this * component, all children of this component, and this component * itself, as follows.

*
    *
  • If the rendered property of this {@link UIComponent} * is false, skip further processing.
  • *
  • Call {@link #pushComponentToEL}.
  • *
  • Call the processValidators() method of all facets * and children of this {@link UIComponent}, in the order determined * by a call to getFacetsAndChildren().
  • *
  • After returning from * calling getFacetsAndChildren() call {@link * UIComponent#popComponentFromEL}.
* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public abstract void processValidators(FacesContext context); /** *

Perform the * component tree processing required by the Update Model * Values phase of the request processing lifecycle for all * facets of this component, all children of this component, and * this component itself, as follows.

*
    *
  • If the rendered property of this {@link * UIComponent} is false, skip further processing.
  • *
  • Call {@link * #pushComponentToEL}.
  • *
  • Call the processUpdates() method of all facets * and children of this {@link UIComponent}, in the order determined * by a call to getFacetsAndChildren(). After returning from the * processUpdates() method on a child or facet, call * {@link UIComponent#popComponentFromEL}
  • *
* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public abstract void processUpdates(FacesContext context); /** *

Perform the * component tree processing required by the state saving portion of * the Render Response phase of the request processing * lifecycle for all facets of this component, all children of this * component, and this component itself, as follows.

*
    * *
  • consult the transient property of this * component. If true, just return null.
  • *
  • Call {@link * #pushComponentToEL}.
  • *
  • Call the processSaveState() method of all facets * and children of this {@link UIComponent} in the order determined * by a call to getFacetsAndChildren(), skipping * children and facets that are transient. Ensure that {@link * #popComponentFromEL} is called correctly after each child or * facet.
  • * *
  • Call the saveState() method of this component.
  • * *
  • Encapsulate the child state and your state into a * Serializable Object and return it.
  • * *
* *

This method may not be called if the state saving method is * set to server.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public abstract Object processSaveState(FacesContext context); // ----------------------------------------------------- Convenience Methods /** *

Convenience method to return the {@link FacesContext} instance * for the current request.

*/ protected abstract FacesContext getFacesContext(); /** *

Convenience method to return the {@link Renderer} instance * associated with this component, if any; otherwise, return * null.

* * @param context {@link FacesContext} for the current request */ protected abstract Renderer getRenderer(FacesContext context); // --------------------------------------------------------- Package Private static final class ComponentSystemEventListenerAdapter implements ComponentSystemEventListener, SystemEventListener, StateHolder, FacesWrapper { ComponentSystemEventListener wrapped; Class instanceClass; // -------------------------------------------------------- Constructors ComponentSystemEventListenerAdapter() { // necessary for state saving } ComponentSystemEventListenerAdapter(ComponentSystemEventListener wrapped, UIComponent component) { this.wrapped = wrapped; this.instanceClass = component.getClass(); } // ------------------------------------ Methods from SystemEventListener public void processEvent(SystemEvent event) throws AbortProcessingException { wrapped.processEvent((ComponentSystemEvent) event); } // ------------------------------------ Methods from SystemEventListener public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { wrapped.processEvent(event); } public boolean isListenerForSource(Object component) { if (wrapped instanceof SystemEventListener) { return ((SystemEventListener) wrapped).isListenerForSource(component); } else { return instanceClass.isAssignableFrom(component.getClass()); } } // -------------------------------------------- Methods from StateHolder public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } return new Object[] { ((wrapped instanceof UIComponent) ? null : new StateHolderSaver(context, wrapped)), instanceClass }; } public void restoreState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } if (state == null) { return; } Object[] s = (Object[]) state; Object listener = s[0]; wrapped = (ComponentSystemEventListener) ((listener == null) ? UIComponent .getCurrentComponent(context) : ((StateHolderSaver) listener).restore(context)); instanceClass = (Class) s[1]; } public boolean isTransient() { if (wrapped instanceof StateHolder) { return ((StateHolder) wrapped).isTransient(); } return false; } public void setTransient(boolean newTransientValue) { // no-op } // ------------------------------------------- Methods from FacesWrapper public ComponentSystemEventListener getWrapped() { return wrapped; } // ------------------------------------------------------ Public Methods @Override public int hashCode() { return (wrapped.hashCode() ^ instanceClass.hashCode()); } @Override public boolean equals(Object obj) { if (!(obj instanceof ComponentSystemEventListenerAdapter)) { return false; } ComponentSystemEventListenerAdapter in = (ComponentSystemEventListenerAdapter) obj; return (wrapped.equals(in.wrapped) && instanceClass.equals(in.instanceClass)); } } // END ComponentSystemEventListenerAdapter }