/* * 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.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 static com.sun.faces.util.Util.isAnyNull; import static com.sun.faces.util.Util.isOneOf; import static java.util.Collections.emptyMap; import static java.util.logging.Level.SEVERE; import static javax.faces.application.Resource.COMPONENT_RESOURCE_KEY; import static javax.faces.component.visit.VisitHint.SKIP_TRANSIENT; import static javax.faces.component.visit.VisitHint.SKIP_UNRENDERED; import static javax.faces.component.visit.VisitResult.ACCEPT; import static javax.faces.component.visit.VisitResult.COMPLETE; import java.io.IOException; import java.io.InputStream; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; 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.Objects; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; import java.util.Set; 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.ComponentSystemEvent; import javax.faces.event.ComponentSystemEventListener; import javax.faces.event.FacesEvent; import javax.faces.event.FacesListener; import javax.faces.event.PostRestoreStateEvent; import javax.faces.event.SystemEvent; import javax.faces.event.SystemEventListener; import javax.faces.event.SystemEventListenerHolder; import javax.faces.render.Renderer; /** * <p> * <strong class="changed_modified_2_0 changed_modified_2_0_rev_a changed_modified_2_1 * changed_modified_2_2 changed_modified_2_3">UIComponent</strong> 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. * </p> * * <p> * 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. * </p> * * <p class="changed_added_2_0"> * If the {@link javax.faces.event.ListenerFor} annotation is attached to the class * definition of a <code>Component</code>, that class must also implement * {@link javax.faces.event.ComponentSystemEventListener}. * </p> * * <p class="changed_added_2_3"> * Dynamically modifying the component tree can happen at any time, during and after * restoring the view, but not during state saving and needs to function properly with * respect to rendering and state saving * </p> */ public abstract class UIComponent implements PartialStateHolder, TransientStateHolder, SystemEventListenerHolder, ComponentSystemEventListener { private static Logger LOGGER = Logger.getLogger("javax.faces.component", "javax.faces.LogStrings"); /** * <p class="changed_added_2_1"> * The <code>ServletContext</code> init parameter consulted by the * <code>UIComponent</code> to tell whether or not the {@link #CURRENT_COMPONENT} and * {@link #CURRENT_COMPOSITE_COMPONENT} attribute keys should be honored as specified. * </p> * * <p> * 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. * </p> */ public static final String HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME = "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES"; /** * <p class="changed_added_2_0"> * The value of this constant is used as the key in the component attribute map, the * value for which is a <code>java.beans.BeanInfo</code> implementation describing the * composite component. This <code>BeanInfo</code> is known as the <em>composite * component BeanInfo</em>. * </p> * * @since 2.0 */ public static final String BEANINFO_KEY = "javax.faces.component.BEANINFO_KEY"; /** * <p class="changed_added_2_0"> * The value of this constant is used as the key in the <em>composite component * BeanDescriptor</em> for the <code>Map<PropertyDescriptor></code> 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}. * </p> * * @since 2.0 */ public static final String FACETS_KEY = "javax.faces.component.FACETS_KEY"; /** * <p class="changed_added_2_0"> * The value of this constant is used as the key in the component attributes * <code>Map</code> for the {@link javax.faces.view.Location} in the view at which * this component instance resides. * </p> * * @since 2.0 */ public static final String VIEW_LOCATION_KEY = "javax.faces.component.VIEW_LOCATION_KEY"; /** * <p class="changed_added_2_0"> * The value of this constant is used as the key in the <em>composite component * BeanDescriptor</em> for a <code>ValueExpression</code> that evaluates to the * <code>component-type</code> of the <em>composite component root</em> * <code>UIComponent</code> for this composite component, if one was declared by the * composite component author. * </p> * * @since 2.0 */ public static final String COMPOSITE_COMPONENT_TYPE_KEY = "javax.faces.component.COMPOSITE_COMPONENT_TYPE"; /** * <p class="changed_added_2_0"> * The value of this constant is used as the key in the <code>Map</code> returned as * described in {@link #FACETS_KEY} for the <code>PropertyDescriptor</code> describing * the composite component facet. The value of this constant is also used as the key * in the <code>Map</code> 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 * <code><composite:implementation></code> section of the <em>composite * component VDL file</em>. * </p> * * @since 2.0 */ public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME"; /** * <p class="changed_added_2_1"> * 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 <code>Collection<String></code> * returned from the <code>getValue()</code> method on the <em>composite component * BeanDescriptor</em>, when this constant is passed as the argument. * </p> * * @since 2.1 */ public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES = "javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES"; /** * 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"; 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<String> attributesThatAreSet; ComponentStateHelper stateHelper; UIComponent compositeParent; private boolean isInView; private Map<String, String> resourceBundleMap; private transient Boolean isSetCurrentComponent; // It is safe to cache this because components never go from being // composite to non-composite. private transient Boolean isCompositeComponent; /** * 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; // -------------------------------------------------------------- Attributes /** * <p> * Return a mutable <code>Map</code> 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 <code>Map</code> methods, plus support the following additional * requirements: * </p> * <ul> * <li>The <code>Map</code> implementation must implement the * <code>java.io.Serializable</code> interface.</li> * <li>Any attempt to add a <code>null</code> key or value must throw a * <code>NullPointerException</code>.</li> * <li>Any attempt to add a key that is not a String must throw a * <code>ClassCastException</code>.</li> * <li>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: * <ul> * <li><code>containsKey</code> - Return <code>false</code>.</li> * <li><code>get()</code> - If the property is readable, call the getter method and * return the returned value (wrapping primitive values in their corresponding wrapper * classes); otherwise throw <code>IllegalArgumentException</code>.</li> * <li><code>put()</code> - 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 <code>null</code>, throw * <code>IllegalArgumentException</code>.</li> * <li><code>remove</code> - Throw <code>IllegalArgumentException</code>.</li> * </ul> * </li> * </ul> * * @return the component attribute map. */ public abstract Map<String, Object> getAttributes(); /** * <p class="changed_added_2_2"> * This is a convenience method that simply calls * {@link #getPassThroughAttributes(boolean)}, passing {@code true} as the argument. * This method must never return {@code null}. * </p> * * @return the pass-through attribute map. * @since 2.2 */ public Map<String, Object> getPassThroughAttributes() { return getPassThroughAttributes(true); } /** * <p class="changed_added_2_2"> * 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}. * </p> * * <div class="changed_added_2_2"> * * <p> * The {@code Map} implementation must implement {@code java.io.Serializable}. * </p> * * <p> * Any attempt to add a {@code null} key or value must throw a * {@code NullPointerException}. * </p> * * <p> * Any attempt to add a key that is not a {@code String} must throw an * {@code IllegalArgumentException}. * </p> * * <p> * For backward compatibility with components that extend directly from this class, a * default implementation is provided that returns the empty map. * </p> * * </div> * * @param create * if <code>true</code>, a new {@code Map} instance will be created if it * does not exist already. If <code>false</code>, and there is no existing * <code>Map</code> instance, one will not be created and <code>null</code> * will be returned. * @return A {@code Map} instance, or {@code null}. * * @since 2.2 */ public Map<String, Object> getPassThroughAttributes(boolean create) { return emptyMap(); } // ---------------------------------------------------------------- Bindings /** * <p> * Return the {@link ValueExpression} used to calculate the value for the specified * attribute or property name, if any. * </p> * * <p> * This method must be overridden and implemented for components that comply with JSF * 1.2 and later. * </p> * * @param name * Name of the attribute or property for which to retrieve a * {@link ValueExpression} * @return the value expression, or <code>null</code>. * @since 1.2 * @throws NullPointerException * if <code>name</code> is <code>null</code> * */ public ValueExpression getValueExpression(String name) { if (name == null) { throw new NullPointerException(); } @SuppressWarnings("unchecked") Map<String, ValueExpression> map = (Map<String, ValueExpression>) getStateHelper().get(UIComponentBase.PropertyKeys.bindings); return map != null ? map.get(name) : null; } /** * <p> * Set the {@link ValueExpression} used to calculate the value for the specified * attribute or property name, if any. * </p> * * <p> * The implementation must call {@link ValueExpression#isLiteralText} on the argument * <code>expression</code>. If <code>isLiteralText()</code> returns <code>true</code>, * invoke {@link ValueExpression#getValue} on the argument expression and pass the * result as the <code>value</code> parameter in a call to <code>this.{@link * #getAttributes()}.put(name, value)</code> where <code>name</code> is the argument * <code>name</code>. 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 <code>isLiteralText()</code> returns <code>false</code>, simply * store the un-evaluated <code>expression</code> argument in the collection of * <code>ValueExpression</code>s under the key given by the argument * <code>name</code>. * </p> * * <p> * This method must be overridden and implemented for components that comply with JSF * 1.2 and later. * </p> * * @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 <code>null</code> to remove any * currently set {@link ValueExpression} * * @throws IllegalArgumentException * if <code>name</code> is one of <code>id</code> or <code>parent</code> * @throws NullPointerException * if <code>name</code> is <code>null</code> * */ public void setValueExpression(String name, ValueExpression binding) { if (name == null) { throw new NullPointerException(); } if (isOneOf(name, "id", "parent")) { throw new IllegalArgumentException(); } if (binding != null) { if (!binding.isLiteralText()) { @SuppressWarnings("unchecked") List<String> sProperties = (List<String>) 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); } else { ELContext context = FacesContext.getCurrentInstance().getELContext(); try { getAttributes().put(name, binding.getValue(context)); } catch (ELException ele) { throw new FacesException(ele); } } } else { getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet, name); getStateHelper().remove(UIComponentBase.PropertyKeys.bindings, name); } } // -------------------------------------------------------------- Properties boolean initialState; /** * <p class="changed_added_2_0"> * 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. * </p> * * @since 2.0 */ @Override public void markInitialState() { initialState = true; } /** * <p class="changed_added_2_0"> * An implementation of {@link PartialStateHolder#initialStateMarked}, this method is * called by the runtime to test if the {@link PartialStateHolder#markInitialState} * method was called. * </p> * * @since 2.0 */ @Override public boolean initialStateMarked() { return initialState; } /** * <p class="changed_added_2_0"> * An implementation of {@link PartialStateHolder#clearInitialState}, this method is * called by the runtime to tell the instance to stop tracking state changes. * </p> * * @since 2.0 */ @Override public void clearInitialState() { initialState = false; } /** * <p class="changed_added_2_0"> * Return the {@link StateHelper} instance used to help this component implement * {@link PartialStateHolder}. * </p> * * @return the state helper. * @since 2.0 */ protected StateHelper getStateHelper() { return getStateHelper(true); } /** * <p class="changed_added_2_0"> * Like {@link #getStateHelper()}, but only create a state helper instance if the * argument <code>creat</code> is <code>true</code>. * </p> * * @param create * if <code>true</code>, a new {@link StateHelper} instance will be created * if it does not exist already. If <code>false</code>, and there is no * existing <code>StateHelper</code> instance, one will not be created and * <code>null</code> will be returned. * * @return the state helper. * @since 2.0 */ protected StateHelper getStateHelper(boolean create) { if (create && stateHelper == null) { stateHelper = new ComponentStateHelper(this); } return stateHelper; } /** * <p class="changed_added_2_1"> * Return the {@link TransientStateHelper} instance for this <code>UIComponent</code> * instance. The default implementation simply calls through to * {@link #getTransientStateHelper(boolean)} passing <code>true</code> as the * argument. * </p> * * @return the transient state helper. * @since 2.1 */ public TransientStateHelper getTransientStateHelper() { return getTransientStateHelper(true); } /** * <p class="changed_added_2_1"> * Return the {@link TransientStateHelper} instance for this <code>UIComponent</code> * instance. * </p> * * @param create * if <code>true</code> create, if necessary, any internal data structures. * If <code>false</code>, do not create any instances. In this case, it is * possible for this method to return <code>null</code>. * @return the transient state helper. * @since 2.1 */ public TransientStateHelper getTransientStateHelper(boolean create) { if (create && stateHelper == null) { stateHelper = new ComponentStateHelper(this); } return stateHelper; } /** * <p class="changed_added_2_1"> * 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}. * </p> * * @since 2.1 */ @Override public void restoreTransientState(FacesContext context, Object state) { boolean forceCreate = (state != null); TransientStateHelper helper = getTransientStateHelper(forceCreate); if (helper != null) { helper.restoreTransientState(context, state); } } /** * <p class="changed_added_2_1"> * 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. * </p> * * @since 2.1 */ @Override public Object saveTransientState(FacesContext context) { TransientStateHelper helper = getTransientStateHelper(false); return helper == null ? null : helper.saveTransientState(context); } /** * <p class="changed_added_2_0"> * Return <code>true</code> if this component is within the view hierarchy otherwise * <code>false</code> * * @return <code>true</code> if within a view hierarchy, <code>false</code> otherwise. * @since 2.0 */ public boolean isInView() { return isInView; } /** * <p class="changed_added_2_0"> * Updates the status as to whether or not this component is currently within the view * hierarchy. <strong>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 <code>List</code> or facet * <code>Map</code></strong>. * </p> * * @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; } /** * <p class="changed_added_2_0"> * Enable EL to access the <code>clientId</code> of a component. This is particularly * useful in combination with the <code>component</code> and <code>cc</code> implicit * objects. A default implementation is provided that simply calls * {@link FacesContext#getCurrentInstance} and then calls through to * {@link #getClientId(FacesContext)}. * </p> * * @return the client id. * @since 2.0 */ public String getClientId() { return getClientId(FacesContext.getCurrentInstance()); } /** * <p> * 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. * </p> * * <p> * The return from this method must be the same value throughout the lifetime of the * instance, unless the <code>id</code> 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: * </p> * * <p> * Find the closest ancestor to <b>this</b> component in the view hierarchy that * implements <code>NamingContainer</code>. Call <code>getContainerClientId()</code> * on it and save the result as the <code>parentId</code> local variable. Call * {@link #getId} on <b>this</b> component and save the result as the * <code>myId</code> local variable. If <code>myId</code> is <code>null</code>, call * <code>context.getViewRoot().createUniqueId()</code> and assign the result to myId. * If <code>parentId</code> is non-<code>null</code>, let <code>myId</code> equal * <code>parentId * + {@link UINamingContainer#getSeparatorChar} + myId</code>. Call * {@link Renderer#convertClientId}, passing <code>myId</code>, and return the result. * </p> * * @param context * The {@link FacesContext} for the current request * @return the client id. * * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract String getClientId(FacesContext context); /** * <p> * Allow components that implement {@link NamingContainer} to selectively disable * prepending their clientId to their descendent's clientIds by breaking the * prepending logic into a separately callable method. See {@link #getClientId} for * usage. * </p> * * <p> * By default, this method will call through to {@link #getClientId} and return the * result. * * @param context * the Faces context. * @return the container client id. * @since 1.2 * * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public String getContainerClientId(FacesContext context) { if (context == null) { throw new NullPointerException(); } return getClientId(context); } /** * <p class="changed_modified_2_3"> * Return the identifier of the component family to which this component belongs. This * identifier, in conjunction with the value of the <code>rendererType</code> * property, may be used to select the appropriate {@link Renderer} for this component * instance. Note this method should NOT return <code>null</code> * </p> * * @return the component family (not null). */ public abstract String getFamily(); /** * <p> * Return the component identifier of this {@link UIComponent}. * </p> * * @return the component identifier. */ public abstract String getId(); /** * <p> * Set the component identifier of this {@link UIComponent} (if any). Component * identifiers must obey the following syntax restrictions: * </p> * <ul> * <li>Must not be a zero-length String.</li> * <li>First character must be a letter or an underscore ('_').</li> * <li>Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash * ('-').</li> * <li> * </ul> * * <p> * Component identifiers must also obey the following semantic restrictions (note that * this restriction is <strong>NOT</strong> enforced by the <code>setId()</code> * implementation): * </p> * <ul> * <li>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}.</li> * </ul> * * @param id * The new component identifier, or <code>null</code> to indicate that this * {@link UIComponent} does not have a component identifier * * @throws IllegalArgumentException * if <code>id</code> is not syntactically valid */ public abstract void setId(String id); /** * <p> * Return the parent {@link UIComponent} of this <code>UIComponent</code>, 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 * <code>getParent( )</code>. * </p> * * @return the parent component. */ public abstract UIComponent getParent(); /** * <p class="changed_modified_2_0"> * <span class="changed_modified_2_0_rev_a">Set</span> the parent * <code>UIComponent</code> of this <code>UIComponent</code>. * <span class="changed_added_2_0 changed_modified_2_0_rev_a">If * <code>parent.isInView()</code> returns <code>true</code>, 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, * <span class="changed_modified_2_0_rev_a">but only if any of the following * conditions are true.</span></span> * </p> * * <div class="changed_modified_2_0_rev_a"> * * <ul> * <li> * <p> * {@link javax.faces.context.FacesContext#getCurrentPhaseId} returns * {@link javax.faces.event.PhaseId#RESTORE_VIEW} and partial state saving is enabled. * </p> * </li> * * <li> * <p> * {@link javax.faces.context.FacesContext#isPostback} returns <code>false</code> and * {@link javax.faces.context.FacesContext#getCurrentPhaseId} returns something other * than {@link javax.faces.event.PhaseId#RESTORE_VIEW} * </p> * </li> * </ul> * * </div> * * * <p class="changed_modified_2_0"> * <strong>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 <code>List</code> or facet <code>Map</code></strong>. * </p> * * @param parent * The new parent, or <code>null</code> for the root node of a component * tree */ public abstract void setParent(UIComponent parent); /** * <p> * Return <code>true</code> if this component (and its children) should be rendered * during the <em>Render Response</em> phase of the request processing lifecycle. * </p> * * @return <code>true</code> if the component should be rendered, <code>false</code> * otherwise. */ public abstract boolean isRendered(); /** * <p> * Set the <code>rendered</code> property of this {@link UIComponent}. * </p> * * @param rendered * If <code>true</code> render this component; otherwise, do not render * this component */ public abstract void setRendered(boolean rendered); /** * <p> * Return the {@link Renderer} type for this {@link UIComponent} (if any). * </p> * * @return the renderer type. */ public abstract String getRendererType(); /** * <p> * Set the {@link Renderer} type for this {@link UIComponent}, or <code>null</code> * for components that render themselves. * </p> * * @param rendererType * Logical identifier of the type of {@link Renderer} to use, or * <code>null</code> for components that render themselves */ public abstract void setRendererType(String rendererType); /** * <p> * 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 <code>true</code> from * this method and rely on {@link UIComponentBase#encodeChildren}. * </p> * * @return <code>true</code> if the component renders its children, <code>false</code> * otherwise. */ public abstract boolean getRendersChildren(); /** * <p class="changed_added_2_0"> * Return a <code>Map<String,String></code> of the <code>ResourceBundle</code> * for this component. A component may have a <code>ResourceBundle</code> associated * with it. This bundle may contain localized properties relating to instances of this * component. The default implementation first looks for a <code>ResourceBundle</code> * with a base name equal to the fully qualified class name of the current * <code>UIComponent this</code> and <code>Locale</code> equal to the * <code>Locale</code> of the current <code>UIViewRoot</code>. If no such bundle is * found, and the component is a composite component, let <em>resourceName</em> be the * <em>resourceName</em> of the {@link Resource} for this composite component, * replacing the file extension with ".properties". Let <em>libraryName</em> be the * <em>libraryName</em> 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 <em>resourceName</em> and <em>libraryName</em>. Note that this * will automatically allow for the localization of the <code>ResourceBundle</code> * due to the localization facility implemented in <code>createResource</code>, 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 <code>InputStream</code> for the * resource is used to create a <code>ResourceBundle</code>. If either of the two * previous steps for obtaining the <code>ResourceBundle</code> for this component is * successful, the <code>ResourceBundle</code> is wrapped in a * <code>Map<String,String></code> and returned. Otherwise * <code>Collections.EMPTY_MAP</code> is returned. * </p> * * @return the resource bundle map. * @since 2.0 */ public Map<String, String> getResourceBundleMap() { if (resourceBundleMap == null) { FacesContext context = FacesContext.getCurrentInstance(); // Step 1: look for a ResourceBundle under the FQCN of this instance ResourceBundle resourceBundle = findResourceBundleUnderFQCNofThis(context); // Step 2: if this is a composite component, look for a // ResourceBundle as a Resource if (resourceBundle == null) { resourceBundle = findResourceBundleAsResource(context); } // Step 3: if the previous steps yielded a ResourceBundle, wrap it // with a Map if (resourceBundle != null) { resourceBundleMap = wrapBundleAsMap(resourceBundle); } if (resourceBundleMap == null) { resourceBundleMap = emptyMap(); } } return resourceBundleMap; } // 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 /** * <p> * <span class="changed_modified_2_0">Return</span> a mutable <code>List</code> * representing the child {@link UIComponent}s associated with this component. The * returned implementation must support all of the standard and optional * <code>List</code> methods, plus support the following additional requirements: * </p> * <ul> * <li>The <code>List</code> implementation must implement the * <code>java.io.Serializable</code> interface.</li> * <li>Any attempt to add a <code>null</code> must throw a NullPointerException</li> * <li>Any attempt to add an object that does not implement {@link UIComponent} must * throw a ClassCastException.</li> * <li>Whenever a new child component is added, the <code>parent</code> property of * the child must be set to this component instance. If the <code>parent</code> * 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).</li> * <li>Whenever an existing child component is removed, the <code>parent</code> * property of the child must be set to <code>null</code>.</li> * * <li class="changed_modified_2_1"> * <p> * 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}<code>.class</code> as the first * argument and the newly added component as the second argument if any the following * cases are true. * </p> * * <ul> * * <li> * <p> * {@link javax.faces.context.FacesContext#getCurrentPhaseId} returns * {@link javax.faces.event.PhaseId#RESTORE_VIEW} and partial state saving is enabled. * </p> * </li> * * <li> * <p> * {@link javax.faces.context.FacesContext#isPostback} returns <code>false</code> and * {@link javax.faces.context.FacesContext#getCurrentPhaseId} returns something other * than {@link javax.faces.event.PhaseId#RESTORE_VIEW} * </p> * </li> * * </ul> * * </li> * * </ul> * * @return the list of children. */ public abstract List<UIComponent> getChildren(); /** * <p> * 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. * </p> * * @return the number of child components. */ public abstract int getChildCount(); /** * <p> * <span class="changed_modified_2_2">Search</span> for and return the * {@link UIComponent} with an <code>id</code> that matches the specified search * expression (if any), according to the algorithm described below. * </p> * * <p class="changed_added_2_2"> * WARNING: The found <code>UIComponent</code> instance, if any, is returned * <strong>without</strong> 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 <code>#{component}</code>, 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 * <strong>does</strong> correctly account for the tree traversal context when * operating on the found <code>UIComponent</code> instance. * {@link #invokeOnComponent} is also useful to find components given a simple * <code>clientId</code>. * * <p> * 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. * </p> * * <p> * A <em>search expression</em> consists of either an identifier (which is matched * exactly against the <code>id</code> 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: * </p> * * <ul> * <li>Identify the {@link UIComponent} that will be the base for searching, by * stopping as soon as one of the following conditions is met: * <ul> * <li>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.</li> * <li>Otherwise, if this {@link UIComponent} is a {@link NamingContainer} it will * serve as the basis.</li> * <li>Otherwise, search up the parents of this component. If a * {@link NamingContainer} is encountered, it will be the base.</li> * <li>Otherwise (if no {@link NamingContainer} is encountered) the root * {@link UIComponent} will be the base.</li> * </ul> * </li> * <li>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 <code>id</code> that matches, within the scope of the base component. * The match is performed as follows: * <ul> * <li>If the search expression is a simple identifier, this value is compared to the * <code>id</code> 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).</li> * <li>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 * <code>findComponent()</code> method of this {@link NamingContainer} will be called, * passing the remainder of the search expression.</li> * </ul> * </li> * </ul> * * @param expr * Search expression identifying the {@link UIComponent} to be returned * * @return the found {@link UIComponent}, or <code>null</code> 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 <code>expr</code> is <code>null</code> */ public abstract UIComponent findComponent(String expr); /** * <p> * <span class="changed_modified_2_1">Starting</span> at this component in the View * hierarchy, search for a component with a <code>clientId</code> equal to the * argument <code>clientId</code> and, if found, call the * {@link ContextCallback#invokeContextCallback} method on the argument * <code>callback</code>, 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. * </p> * * <p> * The default implementation will first check if <code>this.getClientId()</code> is * equal to the argument <code>clientId</code>. If so, * <span class="changed_added_2_1">first call {@link #pushComponentToEL}, then</span> * call the {@link ContextCallback#invokeContextCallback} method on the argument * callback, passing through the <code>FacesContext</code> argument and passing this * as the component argument. <span class="changed_added_2_1">Then call * {@link #popComponentFromEL}</span>. If an <code>Exception</code> is thrown by the * callback, wrap it in a {@link FacesException} and re-throw it. Otherwise, return * <code>true</code>. * </p> * * <p> * Otherwise, for each component returned by {@link #getFacetsAndChildren}, call * <code>invokeOnComponent()</code> passing the arguments to this method, in order. * The first time <code>invokeOnComponent()</code> returns true, abort traversing the * rest of the <code>Iterator</code> and return <code>true</code>. * </p> * * <p> * 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 <code>clientId</code>. For example, an iterating * component such as {@link UIData} will need to set its row index to correctly * reflect the argument <code>clientId</code> before finding the appropriate child * component backed by the correct row. When the callback returns, either normally or * by throwing an <code>Exception</code> the implementation of this method must * restore the state of the view to the way it was before invoking the callback. * </p> * * <p> * If none of the elements from {@link #getFacetsAndChildren} returned * <code>true</code> from <code>invokeOnComponent()</code>, return <code>false</code>. * </p> * * <p> * Simple usage example to find a component by <code>clientId</code>. * </p> * * <pre> * <code> 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; } }); } * </code> * </pre> * * * * @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 * <code>FacesException</code> and re-thrown. * * @return <code>true</code> if the a component with the given <code>clientId</code> * is found, the callback method was successfully invoked passing that * component as an argument, and no Exception was thrown. Returns * <code>false</code> if no component with the given <code>clientId</code> is * found. * */ public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException { if (isAnyNull(context, clientId, callback)) { throw new NullPointerException(); } boolean found = false; if (clientId.equals(getClientId(context))) { try { pushComponentToEL(context, this); callback.invokeContextCallback(context, this); return true; } catch (Exception e) { throw new FacesException(e); } finally { this.popComponentFromEL(context); } } else { Iterator<UIComponent> facetsAndChildrenIterator = getFacetsAndChildren(); while (facetsAndChildrenIterator.hasNext() && !found) { found = facetsAndChildrenIterator.next().invokeOnComponent(context, clientId, callback); } } return found; } // ------------------------------------------------ Facet Management Methods /** * <p> * Return a mutable <code>Map</code> 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 * <code>Map</code> methods, plus support the following additional requirements: * </p> * * <ul> * <li>The <code>Map</code> implementation must implement the * <code>java.io.Serializable</code> interface.</li> * <li>Any attempt to add a <code>null</code> key or value must throw a * NullPointerException.</li> * <li>Any attempt to add a key that is not a String must throw a * ClassCastException.</li> * <li>Any attempt to add a value that is not a {@link UIComponent} must throw a * ClassCastException.</li> * <li>Whenever a new facet {@link UIComponent} is added: * <ul> * <li>The <code>parent</code> property of the component must be set to this component * instance.</li> * <li>If the <code>parent</code> 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).</li> * </ul> * </li> * * <li>Whenever an existing facet {@link UIComponent} is removed: * <ul> * <li>The <code>parent</code> property of the facet must be set to * <code>null</code>.</li> * </ul> * </li> * </ul> * * @return the map of facets. */ public abstract Map<String, UIComponent> getFacets(); /** * <p> * 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. * </p> * * <p> * For backwards compatability with classes that extend UIComponent directly, a * default implementation is provided that simply calls {@link #getFacets} and then * calls the <code>size()</code> method on the returned <code>Map</code>. A more * optimized version of this method is provided in * {@link UIComponentBase#getFacetCount}. * * @return the number of facets. * @since 1.2 */ public int getFacetCount() { return getFacets().size(); } /** * <p> * Convenience method to return the named facet, if it exists, or <code>null</code> * otherwise. If the requested facet does not exist, the facets Map must not be * created. * </p> * * @param name * Name of the desired facet * @return the component, or <code>null</code>. */ public abstract UIComponent getFacet(String name); /** * <p> * Return an <code>Iterator</code> 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 * <code>Iterator</code> is returned. * </p> * * <p> * The returned <code>Iterator</code> must not support the <code>remove()</code> * operation. * </p> * * @return the facets and children iterator. */ public abstract Iterator<UIComponent> getFacetsAndChildren(); // -------------------------------------------- Lifecycle Processing Methods /** * <p> * 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. * </p> * <p class="changed_added_2_0"> * If the <code>event</code> is an instance of {@link javax.faces.event.BehaviorEvent} * and the current <code>component</code> is the source of the <code>event</code> call * {@link javax.faces.event.BehaviorEvent#getBehavior} to get the * {@link javax.faces.component.behavior.Behavior} for the event. * * <span class="changed_modified_2_0_rev_a">Call * {@link javax.faces.component.behavior.Behavior#broadcast(javax.faces.event.BehaviorEvent)} * on the <code>Behavior</code> instance</span>. * </p> * * @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 <code>event</code> is <code>null</code> */ public abstract void broadcast(FacesEvent event) throws AbortProcessingException; /** * <p> * Decode any new state of this {@link UIComponent} from the request contained in the * specified {@link FacesContext}, and store this state as needed. * </p> * <p> * During decoding, events may be enqueued for later processing (by event listeners * who have registered an interest), by calling <code>queueEvent()</code>. * </p> * * @param context * {@link FacesContext} for the request we are processing * * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void decode(FacesContext context); /** * <p class="changed_added_2_0"> * Perform a tree visit starting at this node in the tree. * </p> * * <div class="changed_added_2_0"> * * <p> * 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. * </p> * * <p> * UIComponent.visitTree() implementations must call UIComponent.pushComponentToEL() * before performing the visit and UIComponent.popComponentFromEL() after the visit. * </p> * * @param visitContext * the <code>VisitContext</code> for this visit * @param callback * the <code>VisitCallback</code> instance whose <code>visit</code> method * will be called for each node visited. * @return component implementations may return <code>true</code> 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. * * </div> * * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback() * * @since 2.0 */ public boolean visitTree(VisitContext visitContext, 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(visitContext)) { return false; } // Push ourselves to EL before visiting FacesContext facesContext = visitContext.getFacesContext(); pushComponentToEL(facesContext, null); try { // Visit ourselves. Note that we delegate to the // VisitContext to actually perform the visit. VisitResult result = visitContext.invokeVisitCallback(this, callback); // If the visit is complete, short-circuit out and end the visit if (result == COMPLETE) { return true; } // Visit children if necessary if (result == ACCEPT) { Iterator<UIComponent> kids = getFacetsAndChildren(); while (kids.hasNext()) { boolean done = kids.next().visitTree(visitContext, 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; } /** * <p class="changed_added_2_0"> * Return <code>true</code> if this component should be visited, <code>false</code> * 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}. * </p> * * <div class="changed_added_2_0"> * * <p> * If this method returns false, the tree visited is short-circuited such that neither * the component nor any of its descendents will be visited * </p> * * <p> * Custom {@code visitTree()} implementations may call this method to determine * whether the component is visitable before performing any visit-related processing. * </p> * * </div> * * @param context * the Visit context. * @return <code>true</code> if visitable, <code>false</code> otherwise. * @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<VisitHint> hints = context.getHints(); if ((hints.contains(SKIP_UNRENDERED) && !this.isRendered()) || (hints.contains(SKIP_TRANSIENT) && this.isTransient())) { return false; } return true; } /** * <p> * <span class="changed_modified_2_0">If</span> our <code>rendered</code> property is * <code>true</code>, 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}<code>.class</code> as the first * argument and the component instance to be rendered as the second argument. * </p> * * <p> * If a {@link Renderer} is associated with this {@link UIComponent}, the actual * encoding will be delegated to * {@link Renderer#encodeBegin(FacesContext, UIComponent)}. * </p> * * <p class="changed_added_2_0"> * If our <code>rendered</code> property is <code>false</code>, call * {@link #pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)} * and return immediately. * </p> * * @param context * {@link FacesContext} for the response we are creating * * @throws IOException * if an input/output error occurs while rendering * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void encodeBegin(FacesContext context) throws IOException; /** * <p> * If our <code>rendered</code> property is <code>true</code>, render the child * {@link UIComponent}s of this {@link UIComponent}. This method will only be called * if the <code>rendersChildren</code> property is <code>true</code>. * </p> * * <p> * If a {@link Renderer} is associated with this {@link UIComponent}, the actual * encoding will be delegated to * {@link Renderer#encodeChildren(FacesContext, UIComponent)}. * <span class="changed_modified_2_0">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)}.</span> * </p> * * @param context * {@link FacesContext} for the response we are creating * * @throws IOException * if an input/output error occurs while rendering * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void encodeChildren(FacesContext context) throws IOException; /** * <p> * <span class="changed_modified_2_0">If</span> our <code>rendered</code> property is * <code>true</code>, render the ending of the current state of this * {@link UIComponent}. * </p> * * <p> * If a {@link Renderer} is associated with this {@link UIComponent}, the actual * encoding will be delegated to * {@link Renderer#encodeEnd(FacesContext, UIComponent)}. * </p> * * <p class="changed_added_2_0"> * Call {@link UIComponent#popComponentFromEL}. before returning regardless of the * value of the <code>rendered</code> property. * </p> * * @param context * {@link FacesContext} for the response we are creating * * @throws IOException * if an input/output error occurs while rendering * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void encodeEnd(FacesContext context) throws IOException; /** * <p> * If this component returns <code>true</code> from {@link #isRendered}, take the * following action. * </p> * * <p> * Render this component and all its children that return <code>true</code> from * <code>isRendered()</code>, regardless of the value of the * {@link #getRendersChildren} flag. * </p> * * @param context * the Faces context. * @since 1.2 * @throws IOException * if an input/output error occurs while rendering * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public void encodeAll(FacesContext context) throws IOException { if (context == null) { throw new NullPointerException(); } if (!isRendered()) { return; } encodeBegin(context); if (getRendersChildren()) { encodeChildren(context); } else if (getChildCount() > 0) { for (UIComponent kid : getChildren()) { kid.encodeAll(context); } } encodeEnd(context); } @SuppressWarnings("unchecked") private static ArrayDeque<UIComponent> _getComponentELStack(String keyName, Map<Object, Object> contextAttributes) { return (ArrayDeque<UIComponent>) contextAttributes.computeIfAbsent(keyName, e -> new ArrayDeque<>()); } // 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 (isSetCurrentComponent != null) { return isSetCurrentComponent; } Boolean honorComponentAttribute = (Boolean) context.getAttributes().get(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME); if (honorComponentAttribute != null) { return honorComponentAttribute; } return Boolean.valueOf(context.getExternalContext().getInitParameter(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME)); } /** * <p class="changed_added_2_0"> * Push the current <code>UIComponent</code> <code>this</code> to the * {@link FacesContext} attribute map using the key {@link #CURRENT_COMPONENT} saving * the previous <code>UIComponent</code> associated with {@link #CURRENT_COMPONENT} * for a subsequent call to {@link #popComponentFromEL}. * </p> * * <p class="changed_added_2_0"> * This method and <code>popComponentFromEL()</code> form the basis for the contract * that enables the EL Expression "<code>#{component}</code>" to resolve to the * "current" component that is being processed in the lifecycle. The requirements for * when <code>pushComponentToEL()</code> and <code>popComponentFromEL()</code> must be * called are specified as needed in the javadoc for this class. * </p> * * <p class="changed_added_2_0"> * After <code>pushComponentToEL()</code> returns, a call to * {@link #getCurrentComponent} must return <code>this</code> <code>UIComponent</code> * instance until <code>popComponentFromEL()</code> is called, after which point the * previous <code>UIComponent</code> instance will be returned from * <code>getCurrentComponent()</code> * </p> * * @param context * the {@link FacesContext} for the current request * @param component * the <code>component</code> to push to the EL. If <code>component</code> * is <code>null</code> the <code>UIComponent</code> instance that this * call was invoked upon will be pushed to the EL. * * @throws NullPointerException * if <code>context</code> is <code>null</code> * * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 */ public void pushComponentToEL(FacesContext context, UIComponent component) { if (context == null) { throw new NullPointerException(); } if (component == null) { component = this; } Map<Object, Object> contextAttributes = context.getAttributes(); ArrayDeque<UIComponent> 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(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(CURRENT_COMPOSITE_COMPONENT, component); } } } /** * <p class="changed_added_2_0"> * Pop the current <code>UIComponent</code> from the {@link FacesContext} attributes * map so that the previous <code>UIComponent</code>, if any, becomes the current * component. * </p> * * @param context * the {@link FacesContext} for the current request * * @throws NullPointerException * if <code>context</code> is <code>null</code> * * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 */ public 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<Object, Object> contextAttributes = context.getAttributes(); ArrayDeque<UIComponent> 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(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<UIComponent> 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(CURRENT_COMPOSITE_COMPONENT, compositeELStack.peek()); } } } /** * <p class="changed_added_2_0"> * Return <code>true</code> if <code>component</code> is a composite component, * otherwise <code>false</code>. * </p> * * @param component * the {@link UIComponent} to test * @return <code>true</code> if this is a composite component, <code>false</code> * otherwise. * @throws NullPointerException * if <code>component</code> is <code>null</code> * @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; } /** * <p> * Finds the nearest composite component parent of the specified component. * </p> * * @param component * the component from which to start the search from * * @return if <code>component</code> is <code>null</code>, return <code>null</code>, * otherwise search the component's parent hierachy for the nearest parent * composite component. If no parent composite component is found, return * <code>null</code> * * @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; } } /** * <p class="changed_added_2_0"> * Return the <code>UIComponent</code> instance that is currently processing. This is * equivalent to evaluating the EL expression "<code>#{component}</code>" and doing a * <code>getValue</code> operation on the resultant <code>ValueExpression</code>. * </p> * * <p class="changed_added_2_0"> * This method must return <code>null</code> if there is no currently processing * <code>UIComponent</code> * </p> * * @param context * {@link FacesContext} for the request we are processing * @return the current component, or <code>null</code>. * @throws NullPointerException * if <code>context</code> is <code>null</code> * * @since 2.0 */ public static UIComponent getCurrentComponent(FacesContext context) { Map<Object, Object> contextAttributes = context.getAttributes(); ArrayDeque<UIComponent> componentELStack = _getComponentELStack(_CURRENT_COMPONENT_STACK_KEY, contextAttributes); return componentELStack.peek(); } /** * <p class="changed_added_2_0"> * Return the closest ancestor component, relative to the component returned from * {@link #getCurrentComponent}, that is a composite component, or <code>null</code> * if no such component exists. * </p> * * @param context * {@link FacesContext} for the request we are processing * @return the current composite component, or <code>null</code>. * @throws NullPointerException * if <code>context</code> is <code>null</code> * * @since 2.0 */ public static UIComponent getCurrentCompositeComponent(FacesContext context) { return _getComponentELStack(_CURRENT_COMPOSITE_COMPONENT_STACK_KEY, context.getAttributes()).peek(); } // -------------------------------------------------- Event Listener Methods /** * <p> * Add the specified {@link FacesListener} to the set of listeners registered to * receive event notifications from this {@link UIComponent}. It is expected that * {@link UIComponent} classes acting as event sources will have corresponding * typesafe APIs for registering listeners of the required type, and the * implementation of those registration methods will delegate to this method. For * example: * </p> * * <pre> * public class FooEvent extends FacesEvent { ... } * * 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); * } * ... * } * </pre> * * @param listener * The {@link FacesListener} to be registered * * @throws NullPointerException * if <code>listener</code> is <code>null</code> */ protected abstract void addFacesListener(FacesListener listener); /** * <p> * 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 <code>clazz</code>. * </p> * * @param clazz * Class that must be implemented by a {@link FacesListener} for it to be * returned * @return the Faces listeners, or a zero-length array. * @throws IllegalArgumentException * if <code>class</code> is not, and does not implement, * {@link FacesListener} * @throws NullPointerException * if <code>clazz</code> is <code>null</code> */ protected abstract FacesListener[] getFacesListeners(Class clazz); /** * <p> * Remove the specified {@link FacesListener} from the set of listeners registered to * receive event notifications from this {@link UIComponent}. * * @param listener * The {@link FacesListener} to be deregistered * * @throws NullPointerException * if <code>listener</code> is <code>null</code> */ protected abstract void removeFacesListener(FacesListener listener); /** * <p> * 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 <code>queueEvent()</code> method of the parent {@link UIComponent}. * </p> * * @param event * {@link FacesEvent} to be queued * * @throws IllegalStateException * if this component is not a descendant of a {@link UIViewRoot} * @throws NullPointerException * if <code>event</code> is <code>null</code> */ public abstract void queueEvent(FacesEvent event); /** * <p class="changed_modified_2_1"> * This implementation throws <code>UnsupportedOperationException</code> and is * provided for the sole purpose of not breaking existing applications that extend * this class. {@link UIComponentBase} provides the implementation of this method. * </p> * * @param eventClass * the event class. * @param componentListener * the listener. * @since 2.1 */ public void subscribeToEvent(Class<? extends SystemEvent> eventClass, ComponentSystemEventListener componentListener) { throw new UnsupportedOperationException(); } /** * <p class="changed_modified_2_1"> * This implementation throws <code>UnsupportedOperationException</code> and is * provided for the sole purpose of not breaking existing applications that extend * this class. {@link UIComponentBase} provides the implementation of this method. * </p> * * @param eventClass * the event class. * @param componentListener * the component listener. * @since 2.1 */ public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass, ComponentSystemEventListener componentListener) { throw new UnsupportedOperationException(); } /** * <p class="changed_modified_2_1"> * This implementation throws <code>UnsupportedOperationException</code> and is * provided for the sole purpose of not breaking existing applications that extend * this class. {@link UIComponentBase} provides the implementation of this method. * </p> * * @param eventClass * the event class. * @return the list of listeners. * @since 2.1 */ @Override public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass) { throw new UnsupportedOperationException(); } /** * <p class="changed_added_2_0"> * Starting with "this", return the closest component in the ancestry that is a * <code>NamingContainer</code> or <code>null</code> if none can be found. * </p> * * @return the naming container, or <code>null</code>. * @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 /** * <p> * <span class="changed_modified_2_0">Perform</span> the component tree processing * required by the <em>Restore View</em> phase of the request processing lifecycle for * all facets of this component, all children of this component, and this component * itself, as follows. * </p> * <ul> * <li class="changed_modified_2_0">Call the <code>restoreState()</code> method of * this component.</li> * * <li class="changed_added_2_0">Call {@link UIComponent#pushComponentToEL}.</li> * * <li>Call the <code>processRestoreState()</code> method of all facets and children * of this {@link UIComponent} in the order determined by a call to * <code>getFacetsAndChildren()</code>. <span class="changed_added_2_0">After * returning from the <code>processRestoreState()</code> method on a child or facet, * call {@link UIComponent#popComponentFromEL}</span></li> * </ul> * * <p> * This method may not be called if the state saving method is set to server. * </p> * * @param context * {@link FacesContext} for the request we are processing * @param state * the state. * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void processRestoreState(FacesContext context, Object state); /** * <p> * <span class="changed_modified_2_0">Perform</span> the component tree processing * required by the <em>Apply Request Values</em> phase of the request processing * lifecycle for all facets of this component, all children of this component, and * this component itself, as follows. * </p> * * <ul> * <li>If the <code>rendered</code> property of this {@link UIComponent} is * <code>false</code>, skip further processing.</li> * <li class="changed_added_2_0">Call {@link #pushComponentToEL}.</li> * * <li>Call the <code>processDecodes()</code> method of all facets and children of * this {@link UIComponent}, in the order determined by a call to * <code>getFacetsAndChildren()</code>.</li> * * <li>Call the <code>decode()</code> method of this component.</li> * * <li>Call {@link #popComponentFromEL} from inside of a * <code>finally block, just before returning.</code></li> * * <li>If a <code>RuntimeException</code> is thrown during decode processing, call * {@link FacesContext#renderResponse} and re-throw the exception.</li> * </ul> * * @param context * {@link FacesContext} for the request we are processing * * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void processDecodes(FacesContext context); /** * <p class="changed_added_2_0"> * The default implementation performs the following action. If the argument * <code>event</code> is an instance of {@link PostRestoreStateEvent}, call * <code>this.</code>{@link #getValueExpression} passing the literal string * “binding”, without the quotes, as the argument. If the result is * non-<code>null</code>, set the value of the <code>ValueExpression</code> to be * <code>this</code>. * </p> */ @Override public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { if (event instanceof PostRestoreStateEvent) { // If this component has a component value reference expression, // make sure to populate the ValueExpression for it. ValueExpression valueExpression = getValueExpression("binding"); if (valueExpression != null) { valueExpression.setValue(FacesContext.getCurrentInstance().getELContext(), this); } isCompositeComponent = null; } } /** * <p> * <span class="changed_modified_2_0 changed_modified_2_3"><span class= * "changed_modified_2_0_rev_a">Perform</span></span> the component tree processing * required by the <em>Process Validations</em> phase of the request processing * lifecycle for all facets of this component, all children of this component, and * this component itself, as follows. * </p> * * <ul> * <li>If the <code>rendered</code> property of this {@link UIComponent} is * <code>false</code>, skip further processing.</li> * <li class="changed_added_2_0">Call {@link #pushComponentToEL}.</li> * <li>Call the <code>processValidators()</code> method of all facets and children of * this {@link UIComponent}, in the order determined by a call to * <code>getFacetsAndChildren()</code>.</li> * <li><span class="changed_modified_2_0_rev_a">After returning from calling * <code>getFacetsAndChildren()</code> call * {@link UIComponent#popComponentFromEL}.</span></li> * </ul> * * @param context * {@link FacesContext} for the request we are processing * * @throws NullPointerException * if <code>context</code> is <code>null</code> * @see javax.faces.event.PreValidateEvent * @see javax.faces.event.PostValidateEvent */ public abstract void processValidators(FacesContext context); /** * <p> * <span class="changed_modified_2_0">Perform</span> the component tree processing * required by the <em>Update Model Values</em> phase of the request processing * lifecycle for all facets of this component, all children of this component, and * this component itself, as follows. * </p> * * <ul> * <li>If the <code>rendered</code> property of this {@link UIComponent} is * <code>false</code>, skip further processing.</li> * * <li class="changed_added_2_0">Call {@link #pushComponentToEL}.</li> * * <li>Call the <code>processUpdates()</code> method of all facets and children of * this {@link UIComponent}, in the order determined by a call to * <code>getFacetsAndChildren()</code>. <span class="changed_added_2_0">After * returning from the <code>processUpdates()</code> method on a child or facet, call * {@link UIComponent#popComponentFromEL}</span></li> * </ul> * * @param context * {@link FacesContext} for the request we are processing * * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract void processUpdates(FacesContext context); /** * <p> * <span class="changed_modified_2_0">Perform</span> the component tree processing * required by the state saving portion of the <em>Render Response</em> phase of the * request processing lifecycle for all facets of this component, all children of this * component, and this component itself, as follows. * </p> * * <ul> * * <li>consult the <code>transient</code> property of this component. If true, just * return <code>null</code>.</li> * * <li class="changed_added_2_0">Call {@link #pushComponentToEL}.</li> * * <li>Call the <code>processSaveState()</code> method of all facets and children of * this {@link UIComponent} in the order determined by a call to * <code>getFacetsAndChildren()</code>, skipping children and facets that are * transient. Ensure that {@link #popComponentFromEL} is called correctly after each * child or facet.</li> * * <li>Call the <code>saveState()</code> method of this component.</li> * * <li>Encapsulate the child state and your state into a Serializable Object and * return it.</li> * * </ul> * * <p> * This method may not be called if the state saving method is set to server. * </p> * * @param context * {@link FacesContext} for the request we are processing * @return the saved state. * @throws NullPointerException * if <code>context</code> is <code>null</code> */ public abstract Object processSaveState(FacesContext context); // ----------------------------------------------------- Convenience Methods /** * <p> * Convenience method to return the {@link FacesContext} instance for the current * request. * </p> * * @return the Faces context. */ protected abstract FacesContext getFacesContext(); /** * <p> * Convenience method to return the {@link Renderer} instance associated with this * component, if any; otherwise, return <code>null</code>. * </p> * * @param context * {@link FacesContext} for the current request * @return the renderer, or <code>null</code>. */ protected abstract Renderer getRenderer(FacesContext context); // --------------------------------------------------------- Package Private static final class ComponentSystemEventListenerAdapter implements ComponentSystemEventListener, SystemEventListener, StateHolder, FacesWrapper<ComponentSystemEventListener> { 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 /** * Process the event. * * @param event * the event. * @throws AbortProcessingException * if the event processing should be aborted. */ @Override public void processEvent(SystemEvent event) throws AbortProcessingException { wrapped.processEvent((ComponentSystemEvent) event); } // ------------------------------------ Methods from SystemEventListener /** * Process the event. * * @param event * the event. * @throws AbortProcessingException * if the event processing should be aborted. */ @Override public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { wrapped.processEvent(event); } /** * Is this a listener for the given component. * * @param component * the component. * @return <code>true</code> if it is a listener, <code>false</code> otherwise. */ @Override public boolean isListenerForSource(Object component) { if (wrapped instanceof SystemEventListener) { return ((SystemEventListener) wrapped).isListenerForSource(component); } return instanceClass.isAssignableFrom(component.getClass()); } // -------------------------------------------- Methods from StateHolder /** * Save the state. * * @param context * the Faces context. * @return the saved state. */ @Override public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } return new Object[] { ((wrapped instanceof UIComponent) ? null : new StateHolderSaver(context, wrapped)), instanceClass }; } /** * Restore the state. * * @param context * the Faces context. * @param state * the state. */ @Override 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]; } /** * Get the transient flag. * * @return <code>true</code> if transient, <code>false</code> otherwise. */ @Override public boolean isTransient() { if (wrapped instanceof StateHolder) { return ((StateHolder) wrapped).isTransient(); } return false; } /** * Set the transient flag. * * <p> * This is a no-op in this case. * </p> * * @param newTransientValue * the new transient flag value. */ @Override public void setTransient(boolean newTransientValue) { // no-op } // ------------------------------------------- Methods from FacesWrapper /** * Get the wrapped ComponentSystemEventListener. * * @return the wrapped ComponentSystemEventListener. */ @Override 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 // --------------------------------------------------------- Private methods private Map<String, String> wrapBundleAsMap(final ResourceBundle bundle) { return new Map<String, String>() { // This is an immutable Map @Override public String toString() { StringBuffer sb = new StringBuffer(); Iterator<Map.Entry<String, String>> entries = this.entrySet().iterator(); Map.Entry<String, String> 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 @Override public void clear() { throw new UnsupportedOperationException(); } @Override public boolean containsKey(Object key) { if (key != null) { return bundle.getString(key.toString()) != null; } return false; } @Override public boolean containsValue(Object value) { Enumeration<String> keys = bundle.getKeys(); while (keys.hasMoreElements()) { if (Objects.equals(value, bundle.getString(keys.nextElement()))) { return true; } } return false; } @Override public Set<Map.Entry<String, String>> entrySet() { HashMap<String, String> mappings = new HashMap<>(); Enumeration<String> keys = bundle.getKeys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); String value = bundle.getString(key); mappings.put(key, value); } return mappings.entrySet(); } @Override @SuppressWarnings("unchecked") public boolean equals(Object obj) { return !((obj == null) || !(obj instanceof Map)) && entrySet().equals(((Map<String, String>) obj).entrySet()); } @Override public String get(Object key) { if (key == null) { return null; } try { return bundle.getString(key.toString()); } catch (MissingResourceException e) { return "???" + key + "???"; } } @Override public int hashCode() { return bundle.hashCode(); } @Override public boolean isEmpty() { return !bundle.getKeys().hasMoreElements(); } @Override public Set<String> keySet() { Set<String> keySet = new HashSet<>(); Enumeration<String> keys = bundle.getKeys(); while (keys.hasMoreElements()) { keySet.add(keys.nextElement()); } return keySet; } // Do not need to implement for immutable Map @Override public String put(String k, String v) { throw new UnsupportedOperationException(); } // Do not need to implement for immutable Map @Override public void putAll(Map<? extends String, ? extends String> m) { throw new UnsupportedOperationException(); } // Do not need to implement for immutable Map @Override public String remove(Object k) { throw new UnsupportedOperationException(); } @Override public int size() { int result = 0; Enumeration<String> keys = bundle.getKeys(); while (keys.hasMoreElements()) { keys.nextElement(); result++; } return result; } @Override public Collection<String> values() { List<String> result = new ArrayList<>(); Enumeration<String> keys = bundle.getKeys(); while (keys.hasMoreElements()) { result.add(bundle.getString(keys.nextElement())); } return result; } }; } private ResourceBundle findResourceBundleUnderFQCNofThis(FacesContext context) { String className = this.getClass().getName(); Locale currentLocale = null; UIViewRoot root = null; ResourceBundle resourceBundle = null; // Step 1: look for a ResourceBundle under the FQCN of this instance if (context != null) { if ((root = context.getViewRoot()) != null) { currentLocale = root.getLocale(); } } if (currentLocale == null) { currentLocale = Locale.getDefault(); } try { resourceBundle = ResourceBundle.getBundle(className, currentLocale); } catch (MissingResourceException e) { // It is not an error if there is no ResourceBundle } return resourceBundle; } private ResourceBundle findResourceBundleAsResource(FacesContext context) { if (getAttributes().containsKey(COMPONENT_RESOURCE_KEY)) { Resource ccResource = (Resource) this.getAttributes().get(COMPONENT_RESOURCE_KEY); if (ccResource != null) { ccResource = findComponentResourceBundleLocaleMatch(context, ccResource.getResourceName(), ccResource.getLibraryName()); if (ccResource != null) { try (InputStream propertiesInputStream = ccResource.getInputStream()) { return new PropertyResourceBundle(propertiesInputStream); } catch (IOException ex) { Logger.getLogger(UIComponent.class.getName()).log(SEVERE, null, ex); } } } } return null; } // 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(SEVERE, null, ex); } finally { if (propertiesInputStream != null) { try { propertiesInputStream.close(); } catch (IOException ioe) { if (LOGGER.isLoggable(SEVERE)) { LOGGER.log(SEVERE, null, ioe); } } } } } } return resourceBundle != null ? result : null; } // ------------------------------------------- Deprecated code /** * <p class="changed_added_2_0"> * <span class="changed_deleted_2_2">The</span> key to which the * <code>UIComponent</code> currently being processed will be associated with within * the {@link FacesContext} attributes map. <span class="changed_deleted_2_2">The use * of this constant is deprecated. Please see * {@link #HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} to enable its use.</span> * </p> * * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 * * @deprecated */ public static final String CURRENT_COMPONENT = "javax.faces.component.CURRENT_COMPONENT"; /** * <p class="changed_added_2_0"> * <span class="changed_deleted_2_2">The</span> key to which the <em>composite</em> * <code>UIComponent</code> currently being processed will be associated with within * the {@link FacesContext} attributes map. <span class="changed_deleted_2_2">The use * of this constant is deprecated. Please see * {@link #HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} to enable its use.</span> * </p> * * @see javax.faces.context.FacesContext#getAttributes() * * @since 2.0 * * @deprecated */ public static final String CURRENT_COMPOSITE_COMPONENT = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT"; /** * * <p> * 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 <code>ValueBinding</code> instance and return it. Otherwise, wrap the result in * an implementation of <code>ValueBinding</code>, and return it. * </p> * * @param name * Name of the attribute or property for which to retrieve a * {@link ValueBinding} * @return the value binding. * @throws NullPointerException * if <code>name</code> is <code>null</code> * * @deprecated This has been replaced by {@link #getValueExpression}. */ public abstract ValueBinding getValueBinding(String name); /** * <p> * Wrap the argument <code>binding</code> in an implementation of * {@link ValueExpression} and call through to {@link #setValueExpression}. * </p> * * @param name * Name of the attribute or property for which to set a * {@link ValueBinding} * @param binding * The {@link ValueBinding} to set, or <code>null</code> to remove any * currently set {@link ValueBinding} * * @throws IllegalArgumentException * if <code>name</code> is one of <code>id</code> or <code>parent</code> * @throws NullPointerException * if <code>name</code> is <code>null</code> * * @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<String, ValueExpression> bindings = null; }