jakarta.faces.component.UIComponentBase Maven / Gradle / Ivy
Show all versions of myfaces-api Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package jakarta.faces.component;
import org.apache.myfaces.core.api.shared.ComponentUtils;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
import jakarta.el.ValueExpression;
import jakarta.faces.FacesException;
import jakarta.faces.component.behavior.Behavior;
import jakarta.faces.component.behavior.ClientBehavior;
import jakarta.faces.component.visit.VisitCallback;
import jakarta.faces.component.visit.VisitContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.AbortProcessingException;
import jakarta.faces.event.BehaviorEvent;
import jakarta.faces.event.FacesEvent;
import jakarta.faces.event.FacesListener;
import jakarta.faces.event.PostAddToViewEvent;
import jakarta.faces.event.PostValidateEvent;
import jakarta.faces.event.PreRemoveFromViewEvent;
import jakarta.faces.event.PreRenderComponentEvent;
import jakarta.faces.event.PreValidateEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.faces.event.SystemEventListener;
import jakarta.faces.render.RenderKit;
import jakarta.faces.render.Renderer;
import jakarta.faces.view.Location;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import jakarta.faces.event.PhaseId;
import org.apache.myfaces.core.api.shared.lang.Assert;
import org.apache.myfaces.core.api.shared.lang.SharedStringBuilder;
/**
* Standard implementation of the UIComponent base class; all standard Faces components extend this class.
*
* Disclaimer: The official definition for the behaviour of this class is the Faces 1.1 specification but for legal
* reasons the specification cannot be replicated here. Any javadoc here therefore describes the current implementation
* rather than the spec, though this class has been verified as correctly implementing the spec.
*
* see Javadoc of Faces Specification for
* more.
*/
@JSFComponent(type = "jakarta.faces.ComponentBase", family = "jakarta.faces.ComponentBase",
desc = "base component when all components must inherit",
tagClass = "jakarta.faces.webapp.UIComponentELTag", configExcluded = true)
@JSFJspProperty(name = "binding", returnType = "jakarta.faces.component.UIComponent",
longDesc = "Identifies a backing bean property (of type UIComponent or appropriate subclass) to bind "
+ "to this component instance. This value must be an EL expression.",
desc = "backing bean property to bind to this component instance")
public abstract class UIComponentBase extends UIComponent
{
private static Logger log = Logger.getLogger(UIComponentBase.class.getName());
private static final String _STRING_BUILDER_KEY
= "jakarta.faces.component.UIComponentBase.SHARED_STRING_BUILDER";
// See ViewPoolProcessor for comments and usages
static final int RESET_MODE_OFF = 0;
static final int RESET_MODE_SOFT = 1;
static final int RESET_MODE_HARD = 2;
private _ComponentAttributesMap _attributesMap = null;
private _PassThroughAttributesMap _passthroughAttributesMap = null;
private List _childrenList = null;
private Map _facetMap = null;
private _DeltaList _facesListeners = null;
private String _clientId = null;
private String _id = null;
private UIComponent _parent = null;
private boolean _transient = false;
private String _rendererType;
private String _markCreated;
private String _facetName;
private int _capabilities = 0;
private final static int FLAG_IS_RENDERER_TYPE_SET = 1;
private final static int FLAG_ADDED_BY_HANDLER = 2;
private final static int FLAG_FACET_CREATED_UIPANEL = 4;
private final static int FLAG_PASSTHROUGH_ATTRIBUTE_MAP_SET = 8;
/**
* This map holds ClientBehavior instances.
*
* Note that BehaviorBase implements PartialStateHolder, so this class
* should deal with that fact on clearInitialState() and
* markInitialState() methods.
*
* Also, the map used by this instance is not set from outside this class.
*
* Note it is possible (but maybe not expected/valid) to manipulate
* the values of the map(the list) but not put instances on the map
* directly, because ClientBehaviorHolder.getClientBehaviors says that
* this method should return a non null unmodificable map.
*
*/
private Map> _behaviorsMap = null;
private transient Map> _unmodifiableBehaviorsMap = null;
private transient FacesContext _facesContext;
private transient Boolean _cachedIsRendered;
private transient Renderer _cachedRenderer;
public UIComponentBase()
{
}
/**
* Set an identifier for this component which is unique within the scope of the nearest ancestor NamingContainer
* component. The id is not necessarily unique across all components in the current view.
*
* The id must start with an underscore if it is generated by the Faces framework, and must not start with an
* underscore if it has been specified by the user (eg in a JSP tag).
*
* The first character of the id must be an underscore or letter. Following characters may be letters, digits,
* underscores or dashes.
*
* Null is allowed as a parameter, and will reset the id to null.
*
* The clientId of this component is reset by this method; see getClientId for more info.
*
* @throws IllegalArgumentException
* if the id is not valid.
*/
@Override
public void setId(String id)
{
isIdValid(id);
_id = id;
_clientId = null;
}
/**
*
Set the parent UIComponent
of this
* UIComponent
.
*
* @param parent The new parent, or null
for the root node
* of a component tree
*/
@Override
public void setParent(UIComponent parent)
{
// removing kids OR this is UIViewRoot
if (parent == null)
{
// not UIViewRoot...
if (_parent != null && _parent.isInView())
{
// trigger the "remove event" lifecycle
// and call setInView(false) for all children/facets
// doing this => recursive
FacesContext facesContext = getFacesContext();
if (facesContext.isProcessingEvents())
{
_publishPreRemoveFromViewEvent(facesContext, this);
}
else
{
_updateInView(this, false);
}
}
_parent = null;
}
else
{
_parent = parent;
if (parent.isInView())
{
// trigger the ADD_EVENT and call setInView(true)
// recursive for all kids/facets...
// Application.publishEvent(java.lang.Class, java.lang.Object) must be called, passing
// PostAddToViewEvent.class as the first argument and the newly added component as the second
// argument.
FacesContext facesContext = parent.isCachedFacesContext() ?
parent.getFacesContext() : getFacesContext();
if (facesContext.isProcessingEvents())
{
_publishPostAddToViewEvent(facesContext, this);
}
else
{
_updateInView(this, true);
}
}
}
}
/**
* Publish PostAddToViewEvent to the component and all facets and children.
*
* @param context
* @param component
*/
private static void _publishPostAddToViewEvent(FacesContext context, UIComponent component)
{
component.setInView(true);
context.getApplication().publishEvent(context, PostAddToViewEvent.class, component.getClass(), component);
if (component.getChildCount() > 0)
{
// PostAddToViewEvent could cause component relocation
// (h:outputScript, h:outputStylesheet, composite:insertChildren, composite:insertFacet)
// so we need to check if the component was relocated or not
List children = component.getChildren();
for (int i = 0; i < children.size(); i++)
{
// spin on same index while component removed/replaced
// to prevent skipping components:
while (true)
{
UIComponent child = children.get(i);
child.pushComponentToEL(context, child);
try
{
_publishPostAddToViewEvent(context, child);
}
finally
{
child.popComponentFromEL(context);
}
if (i < children.size() && children.get(i) != child)
{
continue;
}
break;
}
}
}
if (component.getFacetCount() > 0)
{
for (UIComponent child : component.getFacets().values())
{
child.pushComponentToEL(context, child);
try
{
_publishPostAddToViewEvent(context, child);
}
finally
{
child.popComponentFromEL(context);
}
}
}
}
/**
* Publish PreRemoveFromViewEvent to the component and all facets and children.
*
* @param context
* @param component
*/
private static void _publishPreRemoveFromViewEvent(FacesContext context, UIComponent component)
{
component.setInView(false);
context.getApplication().publishEvent(context, PreRemoveFromViewEvent.class, component.getClass(), component);
if (component.getChildCount() > 0)
{
for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
{
UIComponent child = component.getChildren().get(i);
_publishPreRemoveFromViewEvent(context, child);
}
}
if (component.getFacetCount() > 0)
{
for (UIComponent child : component.getFacets().values())
{
_publishPreRemoveFromViewEvent(context, child);
}
}
}
private static void _updateInView(UIComponent component, boolean isInView)
{
component.setInView(isInView);
if (component.getChildCount() > 0)
{
for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
{
UIComponent child = component.getChildren().get(i);
_updateInView(child, isInView);
}
}
if (component.getFacetCount() > 0)
{
for (UIComponent child : component.getFacets().values())
{
_updateInView(child, isInView);
}
}
}
/**
*
* @param eventName
* @param behavior
*
* @since 2.0
*/
public void addClientBehavior(String eventName, ClientBehavior behavior)
{
Collection eventNames = getEventNames();
if (eventNames == null)
{
throw new IllegalStateException("Attempting to add a Behavior to a component, "
+ "that does not support any event types. getEventTypes() must return a non-null Set.");
}
if (eventNames.contains(eventName))
{
if (_behaviorsMap == null)
{
_behaviorsMap = new HashMap<>(5, 1f);
}
List behaviorsForEvent = _behaviorsMap.get(eventName);
if (behaviorsForEvent == null)
{
// Normally have client only 1 client behaviour per event name, so size 2 must be sufficient:
behaviorsForEvent = new _DeltaList<>(2);
_behaviorsMap.put(eventName, behaviorsForEvent);
}
behaviorsForEvent.add(behavior);
_unmodifiableBehaviorsMap = null;
}
}
/**
* Invoke any listeners attached to this object which are listening for an event whose type matches the specified
* event's runtime type.
*
* This method does not propagate the event up to parent components, ie listeners attached to parent components
* don't automatically get called.
*
* If any of the listeners throws AbortProcessingException then that exception will prevent any further listener
* callbacks from occurring, and the exception propagates out of this method without alteration.
*
* ActionEvent events are typically queued by the renderer associated with this component in its decode method;
* ValueChangeEvent events by the component's validate method. In either case the event's source property references
* a component. At some later time the UIViewRoot component iterates over its queued events and invokes the
* broadcast method on each event's source object.
*
* @param event
* must not be null.
*/
@Override
public void broadcast(FacesEvent event) throws AbortProcessingException
{
Assert.notNull(event, "event");
if (event instanceof BehaviorEvent && event.getComponent() == this)
{
Behavior behavior = ((BehaviorEvent) event).getBehavior();
behavior.broadcast((BehaviorEvent) event);
}
if (_facesListeners == null)
{
return;
}
// perf: _facesListeners is RandomAccess instance (jakarta.faces.component._DeltaList)
for (int i = 0, size = _facesListeners.size(); i < size; i++)
{
FacesListener facesListener = _facesListeners.get(i);
if (event.isAppropriateListener(facesListener))
{
event.processListener(facesListener);
}
}
}
@Override
public void clearInitialState()
{
super.clearInitialState();
if (_facesListeners != null)
{
_facesListeners.clearInitialState();
}
if (_behaviorsMap != null)
{
for (Map.Entry> entry : _behaviorsMap.entrySet())
{
((PartialStateHolder) entry.getValue()).clearInitialState();
}
}
if (_systemEventListenerClassMap != null)
{
for (Map.Entry, List> entry :
_systemEventListenerClassMap.entrySet())
{
((PartialStateHolder) entry.getValue()).clearInitialState();
}
}
_capabilities &= ~(FLAG_IS_RENDERER_TYPE_SET);
}
/**
* Check the submitted form parameters for data associated with this component. This default implementation
* delegates to this component's renderer if there is one, and otherwise ignores the call.
*/
@Override
public void decode(FacesContext context)
{
Assert.notNull(context, "context");
setCachedRenderer(null);
Renderer renderer = getRenderer(context);
if (renderer != null)
{
setCachedRenderer(renderer);
try
{
renderer.decode(context, this);
}
finally
{
setCachedRenderer(null);
}
}
}
@Override
public void encodeAll(FacesContext context) throws IOException
{
Assert.notNull(context, "context");
pushComponentToEL(context, this);
try
{
setCachedIsRendered(null);
boolean rendered;
try
{
setCachedFacesContext(context);
rendered = isRendered();
}
finally
{
setCachedFacesContext(null);
}
setCachedIsRendered(rendered);
if (!rendered)
{
setCachedIsRendered(null);
return;
}
setCachedRenderer(null);
setCachedRenderer(getRenderer(context));
}
finally
{
popComponentFromEL(context);
}
try
{
this.encodeBegin(context);
// rendering children
boolean rendersChildren;
try
{
setCachedFacesContext(context);
rendersChildren = this.getRendersChildren();
}
finally
{
setCachedFacesContext(null);
}
if (rendersChildren)
{
this.encodeChildren(context);
} // let children render itself
else
{
if (this.getChildCount() > 0)
{
for (int i = 0; i < this.getChildCount(); i++)
{
UIComponent comp = this.getChildren().get(i);
comp.encodeAll(context);
}
}
}
this.encodeEnd(context);
}
finally
{
setCachedIsRendered(null);
setCachedRenderer(null);
}
}
@Override
public void encodeBegin(FacesContext context) throws IOException
{
Assert.notNull(context, "context");
try
{
setCachedFacesContext(context);
// Call UIComponent.pushComponentToEL(jakarta.faces.context.FacesContext, jakarta.faces.component.UIComponent)
pushComponentToEL(context, this);
if (isRendered())
{
// If our rendered property is true, render the beginning of the current state of this
// UIComponent to the response contained in the specified FacesContext.
// Call Application.publishEvent(java.lang.Class, java.lang.Object), passing BeforeRenderEvent.class as
// the first argument and the component instance to be rendered as the second argument.
// The main issue we have here is that the listeners are normally just registered
// to UIComponent, how do we deal with inherited ones?
// We have to ask the EG
context.getApplication().publishEvent(context, PreRenderComponentEvent.class, UIComponent.class, this);
Renderer renderer = getRenderer(context);
if (renderer != null)
{
// If a Renderer is associated with this UIComponent, the actual encoding will be delegated to
// Renderer.encodeBegin(FacesContext, UIComponent).
renderer.encodeBegin(context, this);
}
}
}
finally
{
setCachedFacesContext(null);
}
}
@Override
public void encodeChildren(FacesContext context) throws IOException
{
Assert.notNull(context, "context");
boolean isCachedFacesContext = isCachedFacesContext();
try
{
if (!isCachedFacesContext)
{
setCachedFacesContext(context);
}
if (isRendered())
{
// If our rendered property is true, render the child UIComponents of this UIComponent.
Renderer renderer = getRenderer(context);
if (renderer == null)
{
// If no Renderer is associated with this UIComponent, iterate over each of the children of this
// component and call UIComponent.encodeAll(jakarta.faces.context.FacesContext).
if (getChildCount() > 0)
{
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
child.encodeAll(context);
}
}
}
else
{
// If a Renderer is associated with this UIComponent, the actual encoding will be delegated to
// Renderer.encodeChildren(FacesContext, UIComponent).
renderer.encodeChildren(context, this);
}
}
}
finally
{
if (!isCachedFacesContext)
{
setCachedFacesContext(null);
}
}
}
@Override
public void encodeEnd(FacesContext context) throws IOException
{
Assert.notNull(context, "context");
try
{
setCachedFacesContext(context);
if (isRendered())
{
// If our rendered property is true, render the ending of the current state of this UIComponent.
Renderer renderer = getRenderer(context);
if (renderer != null)
{
// If a Renderer is associated with this UIComponent, the actual encoding will be delegated to
// Renderer.encodeEnd(FacesContext, UIComponent).
renderer.encodeEnd(context, this);
}
}
}
finally
{
// Call UIComponent.popComponentFromEL(jakarta.faces.context.FacesContext). before returning regardless
// of the value of the rendered property.
popComponentFromEL(context);
setCachedFacesContext(null);
}
}
/**
* Standard method for finding other components by id, inherited by most UIComponent objects.
*
* The lookup is performed in a manner similar to finding a file in a filesystem; there is a "base" at which to
* start, and the id can be for something in the "local directory", or can include a relative path. Here,
* NamingContainer components fill the role of directories, and ":" is the "path separator". Note, however, that
* although components have a strict parent/child hierarchy, component ids are only prefixed ("namespaced") with the
* id of their parent when the parent is a NamingContainer.
*
* The base node at which the search starts is determined as follows:
*
* - When expr starts with ':', the search starts with the root component of the tree that this component is in
* (ie the ancestor whose parent is null).
*
- Otherwise, if this component is a NamingContainer then the search starts with this component.
*
- Otherwise, the search starts from the nearest ancestor NamingContainer (or the root component if there is no
* NamingContainer ancestor).
*
*
* @param expr
* is of form "id1:id2:id3".
* @return UIComponent or null if no component with the specified id is found.
*/
@Override
public UIComponent findComponent(String expr)
{
Assert.notNull(expr, "expr");
if (expr.length() == 0)
{
return null;
}
char separatorChar = getFacesContext().getNamingContainerSeparatorChar();
UIComponent findBase;
if (expr.charAt(0) == separatorChar)
{
findBase = ComponentUtils.findRootComponent(this);
expr = expr.substring(1);
}
else
{
if (this instanceof NamingContainer)
{
findBase = this;
}
else
{
findBase = ComponentUtils.findClosestNamingContainer(this, true /* root if not found */);
}
}
int separator = expr.indexOf(separatorChar);
if (separator == -1)
{
return ComponentUtils.findComponent(findBase, expr, separatorChar);
}
String id = expr.substring(0, separator);
findBase = ComponentUtils.findComponent(findBase, id, separatorChar);
if (findBase == null)
{
return null;
}
if (!(findBase instanceof NamingContainer))
{
throw new IllegalArgumentException("Intermediate identifier " + id + " in search expression " + expr
+ " identifies a UIComponent that is not a NamingContainer");
}
return findBase.findComponent(expr.substring(separator + 1));
}
/**
* Get a map through which all the UIComponent's properties, value-bindings and non-property attributes can be read
* and written.
*
* When writing to the returned map:
*
* - If this component has an explicit property for the specified key then the setter method is called. An
* IllegalArgumentException is thrown if the property is read-only. If the property is readable then the old value
* is returned, otherwise null is returned.
*
- Otherwise the key/value pair is stored in a map associated with the component.
*
* Note that value-bindings are not written by put calls to this map. Writing to the attributes map using a
* key for which a value-binding exists will just store the value in the attributes map rather than evaluating the
* binding, effectively "hiding" the value-binding from later attributes.get calls. Setter methods on components
* commonly do not evaluate a binding of the same name; they just store the provided value directly on the
* component.
*
* When reading from the returned map:
*
* - If this component has an explicit property for the specified key then the getter method is called. If the
* property exists, but is read-only (ie only a setter method is defined) then an IllegalArgumentException is
* thrown.
*
- If the attribute map associated with the component has an entry with the specified key, then that is
* returned.
*
- If this component has a value-binding for the specified key, then the value-binding is evaluated to fetch the
* value.
*
- Otherwise, null is returned.
*
* Note that components commonly define getter methods such that they evaluate a value-binding of the same name if
* there isn't yet a local property.
*
* Assigning values to the map which are not explicit properties on the underlying component can be used to "tunnel"
* attributes from the JSP tag (or view-specific equivalent) to the associated renderer without modifying the
* component itself.
*
* Any value-bindings and non-property attributes stored in this map are automatically serialized along with the
* component when the view is serialized.
*/
@Override
public Map getAttributes()
{
if (_attributesMap == null)
{
_attributesMap = new _ComponentAttributesMap(this);
}
return _attributesMap;
}
@Override
public Map getPassThroughAttributes(boolean create)
{
// Take into account the param "create" in MyFaces case does not have
// sense at all
if (_passthroughAttributesMap == null)
{
if (!create)
{
if ((_capabilities & FLAG_PASSTHROUGH_ATTRIBUTE_MAP_SET) != 0)
{
// Was already created, return wrapper
_passthroughAttributesMap = new _PassThroughAttributesMap(this);
}
}
else
{
_passthroughAttributesMap = new _PassThroughAttributesMap(this);
_capabilities |= FLAG_PASSTHROUGH_ATTRIBUTE_MAP_SET;
}
}
return _passthroughAttributesMap;
}
/**
* Return the number of direct child components this component has.
*
* Identical to getChildren().size() except that when this component has no children this method will not force an
* empty list to be created.
*/
@Override
public int getChildCount()
{
return _childrenList == null ? 0 : _childrenList.size();
}
/**
* Return a list of the UIComponent objects which are direct children of this component.
*
* The list object returned has some non-standard behaviour:
*
* - The list is type-checked; only UIComponent objects can be added.
*
- If a component is added to the list with an id which is the same as some other component in the list then an
* exception is thrown. However multiple components with a null id may be added.
*
- The component's parent property is set to this component. If the component already had a parent, then the
* component is first removed from its original parent's child list.
*
*/
@Override
public List getChildren()
{
if (_childrenList == null)
{
_childrenList = new _ComponentChildrenList(this);
}
return _childrenList;
}
/**
*
* @return
*
* @since 2.0
*/
public Map> getClientBehaviors()
{
if(_behaviorsMap == null)
{
return Collections.emptyMap();
}
return wrapBehaviorsMap();
}
/**
* Get a string which can be output to the response which uniquely identifies this UIComponent within the current
* view.
*
* The component should have an id attribute already assigned to it; however if the id property is currently null
* then a unique id is generated and set for this component. This only happens when components are programmatically
* created without ids, as components created by a ViewHandler should be assigned ids when they are created.
*
* If this component is a descendant of a NamingContainer then the client id is of form
* "{namingContainerId}:{componentId}". Note that the naming container's id may itself be of compound form if it has
* an ancestor naming container. Note also that this only applies to naming containers; other UIComponent types in
* the component's ancestry do not affect the clientId.
*
* Finally the renderer associated with this component is asked to convert the id into a suitable form. This allows
* escaping of any characters in the clientId which are significant for the markup language generated by that
* renderer.
*/
@Override
public String getClientId(FacesContext context)
{
Assert.notNull(context, "context");
if (_clientId != null)
{
return _clientId;
}
String id = getId();
if (id == null)
{
// Although this is an error prone side effect, we automatically create a new id
// just to be compatible to the RI
// The documentation of UniqueIdVendor says that this interface should be implemented by
// components that also implements NamingContainer. The only component that does not implement
// NamingContainer but UniqueIdVendor is UIViewRoot. Anyway we just can't be 100% sure about this
// fact, so it is better to scan for the closest UniqueIdVendor. If it is not found use
// viewRoot.createUniqueId, otherwise use UniqueIdVendor.createUniqueId(context,seed).
UniqueIdVendor parentUniqueIdVendor = ComponentUtils.findClosest(UniqueIdVendor.class, this);
if (parentUniqueIdVendor == null)
{
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot != null)
{
id = viewRoot.createUniqueId();
}
else
{
// The RI throws a NPE
String location = getComponentLocation(this);
throw new FacesException("Cannot create clientId. No id is assigned for component"
+ " to create an id and UIViewRoot is not defined: "
+ ComponentUtils.getPathToComponent(this)
+ (location != null ? " created from: " + location : ""));
}
}
else
{
id = parentUniqueIdVendor.createUniqueId(context, null);
}
setId(id);
}
UIComponent namingContainer = ComponentUtils.findClosestNamingContainer(this, false);
if (namingContainer != null)
{
String containerClientId = namingContainer.getContainerClientId(context);
if (containerClientId != null)
{
StringBuilder bld = _getSharedStringBuilder(context);
_clientId = bld.append(containerClientId).append(
context.getNamingContainerSeparatorChar()).append(id).toString();
}
else
{
_clientId = id;
}
}
else
{
_clientId = id;
}
Renderer renderer = getRenderer(context);
if (renderer != null)
{
_clientId = renderer.convertClientId(context, _clientId);
}
// -=Leonardo Uribe=- In jsf 1.1 and 1.2 this warning has sense, but in jsf 2.0 it is common to have
// components without any explicit id (UIViewParameter components and UIOuput resource components) instances.
// So, this warning is becoming obsolete in this new context and should be removed.
//if (idWasNull && log.isLoggable(Level.WARNING))
//{
// log.warning("WARNING: Component " + _clientId
// + " just got an automatic id, because there was no id assigned yet. "
// + "If this component was created dynamically (i.e. not by a JSP tag) you should assign it an "
// + "explicit static id or assign it the id you get from "
// + "the createUniqueId from the current UIViewRoot "
// + "component right after creation! Path to Component: " + _ComponentUtils.getPathToComponent(this));
//}
return _clientId;
}
/**
*
* @return
*
* @since 2.0
*/
public String getDefaultEventName()
{
// if a default event exists for a component, this method is overriden thus assume null
return null;
}
/**
*
* @return
*
* @since 2.0
*/
public Collection getEventNames()
{
// must be specified by the implementing component.
// Returning null will force an error message in addClientBehavior.
return null;
}
@Override
public UIComponent getFacet(String name)
{
return _facetMap == null ? null : _facetMap.get(name);
}
/**
* @since 1.2
*/
@Override
public int getFacetCount()
{
return _facetMap == null ? 0 : _facetMap.size();
}
@Override
public Map getFacets()
{
if (_facetMap == null)
{
_facetMap = new _ComponentFacetMap<>(this);
}
return _facetMap;
}
@Override
public Iterator getFacetsAndChildren()
{
// we can't use _facetMap and _childrenList here directly,
// because some component implementation could keep their
// own properties for facets and children and just override
// getFacets() and getChildren() (e.g. seen in PrimeFaces).
// See MYFACES-2611 for details.
if (getFacetCount() == 0)
{
if (getChildCount() == 0)
{
return Collections.emptyIterator();
}
return getChildren().iterator();
}
else
{
if (getChildCount() == 0)
{
return getFacets().values().iterator();
}
return new _FacetsAndChildrenIterator(getFacets(), getChildren());
}
}
/**
* Get a string which uniquely identifies this UIComponent within the scope of the nearest ancestor NamingContainer
* component. The id is not necessarily unique across all components in the current view.
*/
@JSFProperty(rtexprvalue = true)
@Override
public String getId()
{
return _id;
}
@Override
public UIComponent getParent()
{
return _parent;
}
@Override
public String getRendererType()
{
// rendererType is literal-only, no ValueExpression - MYFACES-3136:
// Even if this is true, according to Faces spec section 8 Rendering Model,
// this part is essential to implement "delegated implementation" pattern,
// so we can't do this optimization here. Instead, Faces developers could prevent
// this evaluation overriding this method directly.
if (_rendererType != null)
{
return _rendererType;
}
ValueExpression expression = getValueExpression("rendererType");
if (expression != null)
{
return expression.getValue(getFacesContext().getELContext());
}
return null;
}
/**
* Indicates whether this component or its renderer manages the invocation of the rendering methods of its child
* components. When this is true:
*
* - This component's encodeBegin method will only be called after all the child components have been created and
* added to this component.
- This component's encodeChildren method will be called after its encodeBegin method.
* Components for which this method returns false do not get this method invoked at all.
- No rendering methods
* will be called automatically on child components; this component is required to invoke the
* encodeBegin/encodeEnd/etc on them itself.
*
*/
@Override
public boolean getRendersChildren()
{
Renderer renderer = getRenderer(getFacesContext());
return renderer != null ? renderer.getRendersChildren() : false;
}
/**
* invokeOnComponent
must be implemented in UIComponentBase
too...
*/
@Override
public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
throws FacesException
{
if (isCachedFacesContext())
{
return super.invokeOnComponent(context, clientId, callback);
}
else
{
try
{
setCachedFacesContext(context);
return super.invokeOnComponent(context, clientId, callback);
}
finally
{
setCachedFacesContext(null);
}
}
}
@Override
public boolean visitTree(VisitContext context, VisitCallback callback)
{
if (isCachedFacesContext())
{
return super.visitTree(context, callback);
}
else
{
try
{
setCachedFacesContext(context.getFacesContext());
return super.visitTree(context, callback);
}
finally
{
setCachedFacesContext(null);
}
}
}
/**
* A boolean value that indicates whether this component should be rendered. Default value: true.
**/
@Override
@JSFProperty
public boolean isRendered()
{
if (_cachedIsRendered != null)
{
return Boolean.TRUE.equals(_cachedIsRendered);
}
return (Boolean) getStateHelper().eval(PropertyKeys.rendered, DEFAULT_RENDERED);
}
@JSFProperty(literalOnly = true, istransient = true, tagExcluded = true)
@Override
public boolean isTransient()
{
return _transient;
}
@Override
public void markInitialState()
{
super.markInitialState();
// Enable copyFullInitialState behavior when delta is written into this component.
((_DeltaStateHelper)getStateHelper()).setCopyFullInitialState(true);
if (_facesListeners != null)
{
_facesListeners.markInitialState();
}
if (_behaviorsMap != null)
{
for (Map.Entry> entry : _behaviorsMap.entrySet())
{
((PartialStateHolder) entry.getValue()).markInitialState();
}
}
if (_systemEventListenerClassMap != null)
{
for (Map.Entry, List> entry :
_systemEventListenerClassMap.entrySet())
{
((PartialStateHolder) entry.getValue()).markInitialState();
}
}
}
@Override
protected void addFacesListener(FacesListener listener)
{
Assert.notNull(listener, "listener");
if (_facesListeners == null)
{
// How many facesListeners have single component normally?
_facesListeners = new _DeltaList<>(5);
}
_facesListeners.add(listener);
}
@Override
protected FacesContext getFacesContext()
{
if (_facesContext == null)
{
return FacesContext.getCurrentInstance();
}
else
{
return _facesContext;
}
}
// FIXME: Notify EG for generic usage
@Override
protected FacesListener[] getFacesListeners(Class clazz)
{
Assert.notNull(clazz, "clazz");
if (!FacesListener.class.isAssignableFrom(clazz))
{
throw new IllegalArgumentException("Class " + clazz.getName() + " must implement " + FacesListener.class);
}
if (_facesListeners == null)
{
return (FacesListener[]) Array.newInstance(clazz, 0);
}
List lst = null;
// perf: _facesListeners is RandomAccess instance (jakarta.faces.component._DeltaList)
for (int i = 0, size = _facesListeners.size(); i < size; i++)
{
FacesListener facesListener = _facesListeners.get(i);
if (facesListener != null && clazz.isAssignableFrom(facesListener.getClass()))
{
if (lst == null)
{
lst = new ArrayList<>(5);
}
lst.add(facesListener);
}
}
if (lst == null || lst.isEmpty())
{
return (FacesListener[]) Array.newInstance(clazz, 0);
}
return lst.toArray((FacesListener[]) Array.newInstance(clazz, lst.size()));
}
@Override
protected Renderer getRenderer(FacesContext context)
{
Assert.notNull(context, "context");
Renderer renderer = getCachedRenderer();
if (renderer != null)
{
return renderer;
}
String rendererType = getRendererType();
if (rendererType == null)
{
return null;
}
RenderKit renderKit = context.getRenderKit();
renderer = renderKit.getRenderer(getFamily(), rendererType);
if (renderer == null)
{
String location = getComponentLocation(this);
String logStr = "No Renderer found for component " + ComponentUtils.getPathToComponent(this)
+ " (component-family=" + getFamily()
+ ", renderer-type=" + rendererType + ')'
+ (location != null ? " created from: " + location : "");
getFacesContext().getExternalContext().log(logStr);
log.warning(logStr);
}
return renderer;
}
@Override
protected void removeFacesListener(FacesListener listener)
{
Assert.notNull(listener, "listener");
if (_facesListeners != null)
{
_facesListeners.remove(listener);
}
}
@Override
public void queueEvent(FacesEvent event)
{
Assert.notNull(event, "event");
UIComponent parent = getParent();
if (parent == null)
{
throw new IllegalStateException("component is not a descendant of a UIViewRoot");
}
parent.queueEvent(event);
}
@Override
public void processDecodes(FacesContext context)
{
try
{
setCachedFacesContext(context);
// Call UIComponent.pushComponentToEL(jakarta.faces.context.FacesContext, jakarta.faces.component.UIComponent)
pushComponentToEL(context, this);
if (_isPhaseExecutable(context))
{
// Call the processDecodes() method of all facets and children of this UIComponent, in the order
// determined by a call to getFacetsAndChildren().
int facetCount = getFacetCount();
if (facetCount > 0)
{
for (UIComponent facet : getFacets().values())
{
facet.processDecodes(context);
}
}
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
child.processDecodes(context);
}
try
{
// Call the decode() method of this component.
decode(context);
}
catch (RuntimeException e)
{
// If a RuntimeException is thrown during decode processing, call FacesContext.renderResponse()
// and re-throw the exception.
context.renderResponse();
throw e;
}
}
}
finally
{
// Call UIComponent.popComponentFromEL(jakarta.faces.context.FacesContext) from inside of a finally
// block, just before returning.
popComponentFromEL(context);
setCachedFacesContext(null);
}
}
@Override
public void processValidators(FacesContext context)
{
try
{
setCachedFacesContext(context);
// Call UIComponent.pushComponentToEL(jakarta.faces.context.FacesContext, jakarta.faces.component.UIComponent)
pushComponentToEL(context, this);
if (_isPhaseExecutable(context))
{
//Pre validation event dispatch for component
context.getApplication().publishEvent(context, PreValidateEvent.class, getClass(), this);
try
{
// Call the processValidators() method of all facets and children of this UIComponent, in the order
// determined by a call to getFacetsAndChildren().
int facetCount = getFacetCount();
if (facetCount > 0)
{
for (UIComponent facet : getFacets().values())
{
facet.processValidators(context);
}
}
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
child.processValidators(context);
}
}
finally
{
context.getApplication().publishEvent(context, PostValidateEvent.class, getClass(), this);
}
}
}
finally
{
popComponentFromEL(context);
setCachedFacesContext(null);
}
}
/**
* This isn't an input component, so just pass on the processUpdates call to child components and facets that might
* be input components.
*
* Components that were never rendered can't possibly be receiving update data (no corresponding fields were ever
* put into the response) so if this component is not rendered then this method does not invoke processUpdates on
* its children.
*/
@Override
public void processUpdates(FacesContext context)
{
try
{
setCachedFacesContext(context);
// Call UIComponent.pushComponentToEL(jakarta.faces.context.FacesContext, jakarta.faces.component.UIComponent)
pushComponentToEL(context, this);
if (_isPhaseExecutable(context))
{
// Call the processUpdates() method of all facets and children of this UIComponent, in the order
// determined by a call to getFacetsAndChildren().
int facetCount = getFacetCount();
if (facetCount > 0)
{
for (UIComponent facet : getFacets().values())
{
facet.processUpdates(context);
}
}
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
child.processUpdates(context);
}
}
}
finally
{
// After returning from the processUpdates() method on a child or facet, call
// UIComponent.popComponentFromEL(jakarta.faces.context.FacesContext)
popComponentFromEL(context);
setCachedFacesContext(null);
}
}
@Override
public Object processSaveState(FacesContext context)
{
Assert.notNull(context, "context");
if (isTransient())
{
// consult the transient property of this component. If true, just return null.
return null;
}
// Call UIComponent.pushComponentToEL(jakarta.faces.context.FacesContext, jakarta.faces.component.UIComponent)
pushComponentToEL(context, this);
Map facetMap;
List