javax.faces.component.UIComponent Maven / Gradle / Ivy
Show all versions of javax.faces-api Show documentation
/*
* 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 java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.FacesWrapper;
import javax.faces.application.Resource;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitHint;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PostRestoreStateEvent;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ComponentSystemEventListener;
import javax.faces.event.FacesEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.event.FacesListener;
import javax.faces.event.SystemEventListenerHolder;
import javax.faces.render.Renderer;
/**
* UIComponent is the base class for all
* user interface components in JavaServer Faces. The set of {@link
* UIComponent} instances associated with a particular request and
* response are organized into a component tree under a {@link
* UIViewRoot} that represents the entire content of the request or
* response.
*
* For the convenience of component developers,
* {@link UIComponentBase} provides the default
* behavior that is specified for a {@link UIComponent}, and is the base class
* for all of the concrete {@link UIComponent} "base" implementations.
* Component writers are encouraged to subclass
* {@link UIComponentBase}, instead of directly
* implementing this abstract class, to reduce the impact of any future changes
* to the method signatures.
*
* If the {@link
* javax.faces.event.ListenerFor} annotation is attached to the class
* definition of a Component
, that class must also
* implement {@link javax.faces.event.ComponentSystemEventListener}.
*
*
* 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
*
*/
public abstract class UIComponent implements PartialStateHolder, TransientStateHolder, SystemEventListenerHolder,
ComponentSystemEventListener {
private static Logger LOGGER = Logger.getLogger("javax.faces.component",
"javax.faces.LogStrings");
/**
* The ServletContext
init
* parameter consulted by
* the UIComponent
to tell whether or not the
* {@link #CURRENT_COMPONENT} and {@link #CURRENT_COMPOSITE_COMPONENT}
* attribute keys should be honored as specified.
*
* If this parameter is not specified, or is set to false, the contract
* specified by the {@link #CURRENT_COMPONENT} and
* {@link #CURRENT_COMPOSITE_COMPONENT} method is not honored. If this
* parameter is set to true, the contract is honored.
*/
public static final String HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME =
"javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES";
/**
* The key to which the
* UIComponent
currently being processed will be
* associated with within the {@link FacesContext} attributes
* map. The use of this constant is
* deprecated. Please see {@link
* #HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} to enable its
* use.
*
* @see javax.faces.context.FacesContext#getAttributes()
*
* @since 2.0
*
* @deprecated
*/
public static final String CURRENT_COMPONENT = "javax.faces.component.CURRENT_COMPONENT";
/**
* The key to which the
* composite UIComponent
currently being
* processed will be associated with within the {@link FacesContext}
* attributes map. The use of this
* constant is deprecated. Please see {@link
* #HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} to enable its
* use.
*
* @see javax.faces.context.FacesContext#getAttributes()
*
* @since 2.0
*
* @deprecated
*/
public static final String CURRENT_COMPOSITE_COMPONENT = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT";
/**
* The value of this constant is used as the key in the
* component attribute map, the value for which is a
* java.beans.BeanInfo
implementation describing the composite
* component. This BeanInfo
is known as the
* composite component BeanInfo.
*
* @since 2.0
*/
public static final String BEANINFO_KEY = "javax.faces.component.BEANINFO_KEY";
/**
* The value of this constant is used as the key
* in the composite component BeanDescriptor for the
* Map<PropertyDescriptor>
that contains meta-information
* for the declared facets for this composite component.
* This map must contain an entry under the key {@link #COMPOSITE_FACET_NAME}, even
* if no facets were explicitly declared. See {@link #COMPOSITE_FACET_NAME}.
*
* @since 2.0
*/
public static final String FACETS_KEY = "javax.faces.component.FACETS_KEY";
/**
* The value of this constant is used as the key
* in the component attributes Map
for the
* {@link javax.faces.view.Location} in the view at which this component
* instance resides.
*
* @since 2.0
*/
public static final String VIEW_LOCATION_KEY = "javax.faces.component.VIEW_LOCATION_KEY";
/**
* The value of this constant is used as the key
* in the composite component BeanDescriptor for a
* ValueExpression
that evaluates to the
* component-type
of the composite component root
* UIComponent
for this composite component, if
* one was declared by the composite component author.
*
* @since 2.0
*/
public static final String COMPOSITE_COMPONENT_TYPE_KEY = "javax.faces.component.COMPOSITE_COMPONENT_TYPE";
/**
* The value of this constant is used as the key
* in the Map
returned as described in {@link #FACETS_KEY}
* for the
* PropertyDescriptor
describing the composite component facet.
* The value of this constant is also used as the key in the Map
* returned from {@link #getFacets}. In this case, it refers to the actual
* facet that is the {@link javax.faces.component.UIPanel} that is the parent of the all
* of the components in the <composite:implementation>
* section of the composite component VDL file.
*
* @since 2.0
*/
public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME";
/**
* This constant enables one to quickly discover
* the names of the declared composite component attributes that have been
* given default values by the composite component author. The information
* is exposed as a Collection<String>
returned from the
* getValue()
method on the composite component
* BeanDescriptor, when this constant is passed as the argument.
*
* @since 2.1
*/
public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES =
"javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES";
enum PropertyKeysPrivate {
attributesThatAreSet
}
/**
* Properties that are tracked by state saving.
*/
enum PropertyKeys {
rendered,
attributes,
bindings,
rendererType,
systemEventListeners,
behaviors,
passThroughAttributes
}
/**
* List of attributes that have been set on the component (this
* may be from setValueExpression, the attributes map, or setters
* from the concrete HTML components. This allows
* for faster rendering of attributes as this list is authoritative
* on what has been set.
*/
List attributesThatAreSet;
ComponentStateHelper stateHelper = null;
UIComponent compositeParent;
private transient Boolean isSetCurrentComponent;
// -------------------------------------------------------------- Attributes
/**
* Return a mutable
* Map
representing the attributes
* (and properties, see below) associated wth this {@link UIComponent},
* keyed by attribute name (which must be a String). The returned
* implementation must support all of the standard and optional
* Map
methods, plus support the following additional
* requirements:
*
* - The
Map
implementation must implement
* the java.io.Serializable
interface.
* - Any attempt to add a
null
key or value must
* throw a NullPointerException
.
* - Any attempt to add a key that is not a String must throw
* a
ClassCastException
.
* - If the attribute name specified as a key matches a property
* of this {@link UIComponent}'s implementation class, the following
* methods will have special behavior:
*
* containsKey
- Return false
.
* get()
- If the property is readable, call
* the getter method and return the returned value (wrapping
* primitive values in their corresponding wrapper classes);
* otherwise throw IllegalArgumentException
.
* put()
- If the property is writeable, call
* the setter method to set the corresponding value (unwrapping
* primitive values in their corresponding wrapper classes).
* If the property is not writeable, or an attempt is made to
* set a property of primitive type to null
,
* throw IllegalArgumentException
.
* remove
- Throw
* IllegalArgumentException
.
*
*
*
* @return the component attribute map.
*/
public abstract Map getAttributes();
/**
* This is a convenience method that
* simply calls {@link #getPassThroughAttributes(boolean)}, passing {@code true}
* as the argument. This method must never return {@code null}.
*
* @return the pass-through attribute map.
* @since 2.2
*/
public Map getPassThroughAttributes(){
return getPassThroughAttributes(true);
}
/**
* This method has the same specification as
* {@link #getPassThroughAttributes() } except that it is allowed to return
* {@code null} if and only if the argument {@code create} is {@code false}
* and no pass through attribute data structure exists for this instance.
* The returned {@code Map} implementation must support all of the standard and optional
* {@code Map} methods, plus support the following additional requirements.
* The map must be stored in using {@link #getStateHelper}.
*
*
*
* The {@code Map} implementation must implement {@code java.io.Serializable}.
*
* Any attempt to add a {@code null} key or value must throw a {@code NullPointerException}.
*
* Any attempt to add a key that is not a {@code String} must
* throw an {@code IllegalArgumentException}.
*
* For backward compatibility with components that extend directly from
* this class, a default implementation is provided that returns the empty
* map.
*
*
*
* @param create if true
, a new {@code Map}
* instance will be created if it does not exist already. If
* false
, and there is no existing
* Map
instance, one will not be created and
* null
will be returned.
* @return A {@code Map} instance, or {@code null}.
*
* @since 2.2
*/
public Map getPassThroughAttributes(boolean create) {
return Collections.emptyMap();
}
// ---------------------------------------------------------------- Bindings
/**
*
* Call through to {@link #getValueExpression} and examine the
* result. If the result is an instance of the wrapper class
* mandated in {@link #setValueBinding}, extract the
* ValueBinding
instance and return it. Otherwise,
* wrap the result in an implementation of
* ValueBinding
, and return it.
*
* @param name Name of the attribute or property for which to retrieve a
* {@link ValueBinding}
* @return the value binding.
* @throws NullPointerException if name
* is null
*
* @deprecated This has been replaced by {@link #getValueExpression}.
*/
public abstract ValueBinding getValueBinding(String name);
/**
* Wrap the argument binding
in an implementation of
* {@link ValueExpression} and call through to {@link
* #setValueExpression}.
*
* @param name Name of the attribute or property for which to set a
* {@link ValueBinding}
* @param binding The {@link ValueBinding} to set, or null
* to remove any currently set {@link ValueBinding}
*
* @throws IllegalArgumentException if name
is one of
* id
or parent
* @throws NullPointerException if name
* is null
*
* @deprecated This has been replaced by {@link #setValueExpression}.
*/
public abstract void setValueBinding(String name, ValueBinding binding);
// The set of ValueExpressions for this component, keyed by property
// name This collection is lazily instantiated
// The set of ValueExpressions for this component, keyed by property
// name This collection is lazily instantiated
@Deprecated
protected Map bindings = null;
/**
* Return the {@link ValueExpression} used to calculate the value for the
* specified attribute or property name, if any.
*
* This method must be overridden and implemented for components that
* comply with JSF 1.2 and later.
*
* @param name Name of the attribute or property for which to retrieve a
* {@link ValueExpression}
* @return the value expression, or null
.
* @since 1.2
* @throws NullPointerException if name
* is null
*
*/
public ValueExpression getValueExpression(String name) {
if (name == null) {
throw new NullPointerException();
}
Map map = (Map)
getStateHelper().get(UIComponentBase.PropertyKeys.bindings);
return ((map != null) ? map.get(name) : null);
}
/**
* Set the {@link ValueExpression} used to calculate the value
* for the specified attribute or property name, if any.
*
* The implementation must call {@link
* ValueExpression#isLiteralText} on the argument
* expression
. If isLiteralText()
returns
* true
, invoke {@link ValueExpression#getValue} on the
* argument expression and pass the result as the value
* parameter in a call to this.{@link
* #getAttributes()}.put(name, value)
where name
* is the argument name
. If an exception is thrown as
* a result of calling {@link ValueExpression#getValue}, wrap it in
* a {@link javax.faces.FacesException} and re-throw it. If
* isLiteralText()
returns false
, simply
* store the un-evaluated expression
argument in the
* collection of ValueExpression
s under the key given
* by the argument name
.
*
* This method must be overridden and implemented for components that
* comply with JSF 1.2 and later.
*
* @since 1.2
*
* @param name Name of the attribute or property for which to set a
* {@link ValueExpression}
* @param binding The {@link ValueExpression} to set, or null
* to remove any currently set {@link ValueExpression}
*
* @throws IllegalArgumentException if name
is one of
* id
or parent
* @throws NullPointerException if name
* is null
*
*/
public void setValueExpression(String name, ValueExpression binding) {
if (name == null) {
throw new NullPointerException();
} else if ("id".equals(name) || "parent".equals(name)) {
throw new IllegalArgumentException();
}
if (binding != null) {
if (!binding.isLiteralText()) {
//if (bindings == null) {
// //noinspection CollectionWithoutInitialCapacity
// bindings = new HashMap();
//}
// add this binding name to the 'attributesThatAreSet' list
//List sProperties = (List)
// getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet);
List sProperties =
(List) getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet);
if (sProperties == null) {
getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, name);
} else if (!sProperties.contains(name)) {
getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, name);
}
getStateHelper().put(UIComponentBase.PropertyKeys.bindings,
name,
binding);
//bindings.put(name, binding);
} else {
ELContext context =
FacesContext.getCurrentInstance().getELContext();
try {
getAttributes().put(name, binding.getValue(context));
} catch (ELException ele) {
throw new FacesException(ele);
}
}
} else {
//if (bindings != null) {
// remove this binding name from the 'attributesThatAreSet' list
// List sProperties = getAttributesThatAreSet(false);
// if (sProperties != null) {
// sProperties.remove(name);
// }
getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet,
name);
getStateHelper().remove(UIComponentBase.PropertyKeys.bindings, name);
//bindings.remove(name);
// if (bindings.isEmpty()) {
// bindings = null;
// }
}
// }
}
// -------------------------------------------------------------- Properties
boolean initialState;
/**
* An implementation of {@link
* PartialStateHolder#markInitialState}, this method is called by
* the runtime to indicate that the instance should start tracking
* changes to its state.
* @since 2.0
*/
@Override
public void markInitialState() {
initialState = true;
}
/**
* An implementation of {@link
* PartialStateHolder#initialStateMarked}, this method is called by
* the runtime to test if the {@link
* PartialStateHolder#markInitialState} method was called.
* @since 2.0
*/
@Override
public boolean initialStateMarked() {
return initialState;
}
/**
* An implementation of {@link
* PartialStateHolder#clearInitialState}, this method is called by
* the runtime to tell the instance to stop tracking state
* changes.
* @since 2.0
*/
@Override
public void clearInitialState() {
initialState = false;
}
/**
* Return the {@link StateHelper}
* instance used to help this component implement {@link
* PartialStateHolder}.
*
* @return the state helper.
* @since 2.0
*/
protected StateHelper getStateHelper() {
return getStateHelper(true);
}
/**
* Like {@link #getStateHelper()}, but
* only create a state helper instance if the argument
* creat
is true
.
* @param create if true
, a new {@link StateHelper}
* instance will be created if it does not exist already. If
* false
, and there is no existing
* StateHelper
instance, one will not be created and
* null
will be returned.
*
* @return the state helper.
* @since 2.0
*/
protected StateHelper getStateHelper(boolean create) {
if (create && stateHelper == null) {
stateHelper = new ComponentStateHelper(this);
}
return stateHelper;
}
/**
* Return the {@link
* TransientStateHelper} instance for this UIComponent
* instance. The default implementation simply calls through to
* {@link #getTransientStateHelper(boolean)} passing true
* as the argument.
*
* @return the transient state helper.
* @since 2.1
*/
public TransientStateHelper getTransientStateHelper()
{
return getTransientStateHelper(true);
}
/**
* Return the {@link
* TransientStateHelper} instance for this UIComponent
* instance.
*
* @param create if true
create, if necessary, any
* internal data structures. If false
, do not create
* any instances. In this case, it is possible for this method to
* return null
.
* @return the transient state helper.
* @since 2.1
*/
public TransientStateHelper getTransientStateHelper(boolean create) {
if (create && stateHelper == null) {
stateHelper = new ComponentStateHelper(this);
}
return stateHelper;
}
/**
* For components that need to support
* the concept of transient state, this method will restore any
* state saved on a prior call to {@link #saveTransientState}.
*
* @since 2.1
*/
@Override
public void restoreTransientState(FacesContext context, Object state)
{
boolean forceCreate = (state != null);
TransientStateHelper helper = getTransientStateHelper(forceCreate);
if (helper != null) {
helper.restoreTransientState(context, state);
}
}
/**
* For components that need to support
* the concept of transient state, this method will save any state
* that is known to be transient in nature.
*
* @since 2.1
*/
@Override
public Object saveTransientState(FacesContext context)
{
TransientStateHelper helper = getTransientStateHelper(false);
return (helper == null) ? null : helper.saveTransientState(context);
}
private boolean isInView;
/**
* Return true
if this
* component is within the view hierarchy otherwise
* false
*
* @return true
if within a view hierarchy, false
otherwise.
* @since 2.0
*/
public boolean isInView() {
return isInView;
}
/**
*
Updates the status as to whether or
* not this component is currently within the view hierarchy.
* This method must never be called by developers; a {@link
* UIComponent}'s internal implementation will call it as components
* are added to or removed from a parent's child List
* or facet Map
.
*
* @param isInView flag indicating whether or not this component is within
* the view hierachy
*
* @since 2.0
*/
public void setInView(boolean isInView) {
this.isInView = isInView;
}
/**
* Enable EL to access the clientId
* of a component. This is particularly useful in combination with the
* component
and cc
implicit
* objects. A default implementation is provided that simply calls
* {@link FacesContext#getCurrentInstance} and then calls through to
* {@link #getClientId(FacesContext)}.
*
* @return the client id.
* @since 2.0
*/
public String getClientId() {
FacesContext context = FacesContext.getCurrentInstance();
return getClientId(context);
}
/**
* Return a client-side identifier for this component, generating
* one if necessary. The associated {@link Renderer}, if any,
* will be asked to convert the clientId to a form suitable for
* transmission to the client.
*
* The return from this method must be the same value throughout
* the lifetime of the instance, unless the id
property
* of the component is changed, or the component is placed in
* a {@link NamingContainer} whose client ID changes (for example,
* {@link UIData}). However, even in these cases, consecutive
* calls to this method must always return the same value. The
* implementation must follow these steps in determining the
* clientId:
*
* Find the closest ancestor to this component in the view
* hierarchy that implements NamingContainer
. Call
* getContainerClientId()
on it and save the result as
* the parentId
local variable. Call {@link #getId} on
* this component and save the result as the
* myId
local variable. If myId
is
* null
, call
* context.getViewRoot().createUniqueId()
and assign
* the result to myId. If parentId
is
* non-null
, let myId
equal parentId
* + {@link UINamingContainer#getSeparatorChar} + myId
. Call
* {@link Renderer#convertClientId}, passing myId
, and
* return the result.
*
* @param context The {@link FacesContext} for the current request
* @return the client id.
*
* @throws NullPointerException if context
* is null
*/
public abstract String getClientId(FacesContext context);
/**
* Allow components that implement {@link NamingContainer} to
* selectively disable prepending their clientId to their
* descendent's clientIds by breaking the prepending logic into a
* separately callable method. See {@link #getClientId} for usage.
*
* 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 context
is
* null
*/
public String getContainerClientId(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
return this.getClientId(context);
}
/**
*
Return the identifier of the component
* family to which this component belongs. This identifier, in conjunction
* with the value of the rendererType
property, may be used to
* select the appropriate {@link Renderer} for this component instance.
* Note this method should NOT return null
*
* @return the component family (not null).
*/
public abstract String getFamily();
/**
* Return the component identifier of this {@link UIComponent}.
*
* @return the component identifier.
*/
public abstract String getId();
/**
* Set the component identifier of this {@link UIComponent} (if any).
* Component identifiers must obey the following syntax restrictions:
*
* - Must not be a zero-length String.
* - First character must be a letter or an underscore ('_').
* - Subsequent characters must be a letter, a digit,
* an underscore ('_'), or a dash ('-').
* -
*
*
* Component identifiers must also obey the following semantic
* restrictions (note that this restriction is NOT
* enforced by the setId()
implementation):
*
* - The specified identifier must be unique among all the components
* (including facets) that are descendents of the nearest ancestor
* {@link UIComponent} that is a {@link NamingContainer}, or within
* the scope of the entire component tree if there is no such
* ancestor that is a {@link NamingContainer}.
*
*
* @param id The new component identifier, or null
to indicate
* that this {@link UIComponent} does not have a component identifier
*
* @throws IllegalArgumentException if id
is not
* syntactically valid
*/
public abstract void setId(String id);
/**
* Return the parent {@link UIComponent} of this
* UIComponent
, if any. A component must allow child
* components to be added to and removed from the list of children
* of this component, even though the child component returns null
* from getParent( )
.
*
* @return the parent component.
*/
public abstract UIComponent getParent();
/**
* Set the parent
* UIComponent
of this UIComponent
. If
* parent.isInView()
returns true
, calling
* this method will first cause a {@link
* javax.faces.event.PreRemoveFromViewEvent} to be published, for
* this node, and then the children of this node. Then, once the
* re-parenting has occurred, a {@link
* javax.faces.event.PostAddToViewEvent} will be published as well,
* first for this node, and then for the node's children, but only if any of the
* following conditions are true.
*
*
* {@link
* javax.faces.context.FacesContext#getCurrentPhaseId} returns
* {@link javax.faces.event.PhaseId#RESTORE_VIEW} and partial
* state saving is enabled.
* {@link javax.faces.context.FacesContext#isPostback}
* returns false
and {@link
* javax.faces.context.FacesContext#getCurrentPhaseId} returns
* something other than {@link
* javax.faces.event.PhaseId#RESTORE_VIEW}
*
*
* This method must never
* be called by developers; a {@link UIComponent}'s internal
* implementation will call it as components are added to or removed
* from a parent's child List
or facet
* Map
.
*
* @param parent The new parent, or null
for the root node
* of a component tree
*/
public abstract void setParent(UIComponent parent);
/**
* Return true
if this component (and its children)
* should be rendered during the Render Response phase
* of the request processing lifecycle.
*
* @return true
if the component should be rendered, false
otherwise.
*/
public abstract boolean isRendered();
/**
* Set the rendered
property of this
* {@link UIComponent}.
*
* @param rendered If true
render this component;
* otherwise, do not render this component
*/
public abstract void setRendered(boolean rendered);
/**
* Return the {@link Renderer} type for this {@link UIComponent}
* (if any).
*
* @return the renderer type.
*/
public abstract String getRendererType();
/**
* Set the {@link Renderer} type for this {@link UIComponent},
* or null
for components that render themselves.
*
* @param rendererType Logical identifier of the type of
* {@link Renderer} to use, or null
for components
* that render themselves
*/
public abstract void setRendererType(String rendererType);
/**
* Return a flag indicating whether this component is responsible
* for rendering its child components. The default implementation
* in {@link UIComponentBase#getRendersChildren} tries to find the
* renderer for this component. If it does, it calls {@link
* Renderer#getRendersChildren} and returns the result. If it
* doesn't, it returns false. As of version 1.2 of the JavaServer
* Faces Specification, component authors are encouraged to return
* true
from this method and rely on {@link
* UIComponentBase#encodeChildren}.
*
* @return true
if the component renders its children, false
otherwise.
*/
public abstract boolean getRendersChildren();
private Map resourceBundleMap = null;
/**
* Return a
* Map<String,String>
of the
* ResourceBundle
for this component. A component may
* have a ResourceBundle
associated with it. This
* bundle may contain localized properties relating to instances of
* this component. The default implementation first looks for a
* ResourceBundle
with a base name equal to the fully
* qualified class name of the current UIComponent this
* and Locale
equal to the Locale
of the
* current UIViewRoot
. If no such bundle is found, and
* the component is a composite component, let resourceName
* be the resourceName of the {@link Resource} for this
* composite component, replacing the file extension with
* ".properties". Let libraryName be the
* libraryName of the the {@link Resource} for this
* composite component. Call {@link
* javax.faces.application.ResourceHandler#createResource(java.lang.String,java.lang.String)},
* passing the derived resourceName and
* libraryName. Note that this will automatically allow
* for the localization of the ResourceBundle
due to
* the localization facility implemented in
* createResource
, which is specified in section
* JSF.2.6.1.3 of the spec prose document. If the resultant {@link
* Resource} exists and can be found, the InputStream
* for the resource is used to create a ResourceBundle
.
* If either of the two previous steps for obtaining the
* ResourceBundle
for this component is successful, the
* ResourceBundle
is wrapped in a
* Map<String,String>
and returned. Otherwise
* Collections.EMPTY_MAP
is returned.
*
* @return the resource bundle map.
* @since 2.0
*/
public Map getResourceBundleMap() {
if (null == resourceBundleMap) {
// See if there is a ResourceBundle under the FQCN for this class
String className = this.getClass().getName();
Locale currentLocale = null;
FacesContext context = null;
UIViewRoot root = null;
ResourceBundle resourceBundle = null;
// Step 1: look for a ResourceBundle under the FQCN of this instance
if (null != (context = FacesContext.getCurrentInstance())) {
if (null != (root = context.getViewRoot())) {
currentLocale = root.getLocale();
}
}
if (null == currentLocale) {
currentLocale = Locale.getDefault();
}
try {
resourceBundle =
ResourceBundle.getBundle(className, currentLocale);
} catch (MissingResourceException e) {
// It is not an error if there is no ResourceBundle
}
// Step 2: if this is a composite component, look for a
// ResourceBundle as a Resource
if (null == resourceBundle) {
if (this.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)) {
Resource ccResource = (Resource)
this.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
if (null != ccResource) {
if (null != (ccResource =
findComponentResourceBundleLocaleMatch(context,
ccResource.getResourceName(),
ccResource.getLibraryName()))) {
try (InputStream propertiesInputStream = ccResource.getInputStream()) {
resourceBundle = new PropertyResourceBundle(propertiesInputStream);
} catch (IOException ex) {
Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
// Step 3: if the previous steps yielded a ResourceBundle, wrap it
// with a Map
if (null != resourceBundle) {
final ResourceBundle bundle = resourceBundle;
resourceBundleMap =
new Map() {
// this is an immutable Map
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
Iterator> entries =
this.entrySet().iterator();
Map.Entry cur;
while (entries.hasNext()) {
cur = entries.next();
sb.append(cur.getKey()).append(": ").append(cur.getValue()).append('\n');
}
return sb.toString();
}
// Do not need to implement for immutable Map
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
boolean result = false;
if (null != key) {
result = (null != bundle.getObject(key.toString()));
}
return result;
}
@Override
public boolean containsValue(Object value) {
Enumeration keys = bundle.getKeys();
boolean result = false;
while (keys.hasMoreElements()) {
Object curObj = bundle.getObject(keys.nextElement());
if ((curObj == value) ||
((null != curObj) && curObj.equals(value))) {
result = true;
break;
}
}
return result;
}
@Override
public Set> entrySet() {
HashMap mappings = new HashMap<>();
Enumeration keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Object value = bundle.getObject(key);
mappings.put(key, value);
}
return mappings.entrySet();
}
@Override
public boolean equals(Object obj) {
return !((obj == null) || !(obj instanceof Map))
&& entrySet().equals(((Map) obj).entrySet());
}
@Override
public Object get(Object key) {
if (null == key) {
return null;
}
try {
return bundle.getObject(key.toString());
} catch (MissingResourceException e) {
return "???" + key + "???";
}
}
@Override
public int hashCode() {
return bundle.hashCode();
}
@Override
public boolean isEmpty() {
Enumeration keys = bundle.getKeys();
return !keys.hasMoreElements();
}
@Override
public Set keySet() {
Set keySet = new HashSet<>();
Enumeration keys = bundle.getKeys();
while (keys.hasMoreElements()) {
keySet.add(keys.nextElement());
}
return keySet;
}
// Do not need to implement for immutable Map
@Override
public Object put(Object k, Object v) {
throw new UnsupportedOperationException();
}
// Do not need to implement for immutable Map
@Override
public void putAll(Map t) {
throw new UnsupportedOperationException();
}
// Do not need to implement for immutable Map
@Override
public Object remove(Object k) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
int result = 0;
Enumeration keys = bundle.getKeys();
while (keys.hasMoreElements()) {
keys.nextElement();
result++;
}
return result;
}
@Override
public java.util.Collection values() {
ArrayList