javax.faces.component.UIComponentBase 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.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.faces.component;
import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.component.behavior.Behavior;
import javax.faces.component.behavior.ClientBehavior;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.*;
import javax.faces.render.Renderer;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* UIComponentBase is a
* convenience base class that implements the default concrete behavior
* of all methods defined by {@link UIComponent}.
*
* By default, this class defines getRendersChildren()
* to find the renderer for this component and call its
* getRendersChildren()
method. The default implementation
* on the Renderer
returns false
. As of
* version 1.2 of the JavaServer Faces Specification, component authors
* are encouraged to return true
from this method and rely
* on the implementation of {@link #encodeChildren} in this class and in
* the Renderer ({@link Renderer#encodeChildren}). Subclasses that wish
* to manage the rendering of their children should override this method
* to return true
instead.
*/
public abstract class UIComponentBase extends UIComponent {
// -------------------------------------------------------------- Attributes
private static Logger LOGGER = Logger.getLogger("javax.faces.component",
"javax.faces.LogStrings");
private static final String ADDED = UIComponentBase.class.getName() + ".ADDED";
/**
* Each entry is an map of PropertyDescriptor
s describing
* the properties of a concrete {@link UIComponent} implementation, keyed
* by the corresponding java.lang.Class
.
*
* IMPLEMENTATION NOTE - This is implemented as a
* WeakHashMap
so that, even if this class is embedded in a
* container's class loader that is a parent to webapp class loaders,
* references to the classes will eventually expire.
*/
@SuppressWarnings({"CollectionWithoutInitialCapacity"})
private static Map, Map>
descriptors =
new WeakHashMap, Map>();
/**
* Reference to the map of PropertyDescriptor
s for this class
* in the descriptors Map.
*/
private Map pdMap = null;
private Map, List> listenersByEventClass;
/**
* An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods.
*/
private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0];
public UIComponentBase() {
populateDescriptorsMapIfNecessary();
}
private void populateDescriptorsMapIfNecessary() {
Class> clazz = this.getClass();
synchronized(descriptors) {
pdMap = descriptors.get(clazz);
}
if (null != pdMap) {
return;
}
// load the property descriptors for this class.
PropertyDescriptor pd[] = getPropertyDescriptors();
if (pd != null) {
pdMap = new HashMap(pd.length, 1.0f);
for (PropertyDescriptor aPd : pd) {
pdMap.put(aPd.getName(), aPd);
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "fine.component.populating_descriptor_map",
new Object[]{clazz,
Thread.currentThread().getName()});
}
// Check again
Map reCheckMap = null;
synchronized(descriptors) {
reCheckMap = descriptors.get(clazz);
}
if (null != reCheckMap) {
return;
}
synchronized(descriptors) {
descriptors.put(clazz, pdMap);
}
}
}
/**
* Return an array of PropertyDescriptors
for this
* {@link UIComponent}'s implementation class. If no descriptors
* can be identified, a zero-length array will be returned.
*
* @throws FacesException if an introspection exception occurs
*/
private PropertyDescriptor[] getPropertyDescriptors() {
PropertyDescriptor[] pd;
try {
pd = Introspector.getBeanInfo(this.getClass()).
getPropertyDescriptors();
} catch (IntrospectionException e) {
throw new FacesException(e);
}
return (pd);
}
/**
* The Map
containing our attributes, keyed by
* attribute name.
*/
private AttributesMap attributes = null;
public Map getAttributes() {
if (attributes == null) {
attributes = new AttributesMap(this);
}
return (attributes);
}
@Override
public Map getPassThroughAttributes(boolean create) {
Map result = (Map)
this.getStateHelper().get(PropertyKeys.passThroughAttributes);
if (null == result) {
if (create) {
result = new PassThroughAttributesMap();
this.getStateHelper().put(PropertyKeys.passThroughAttributes,
result);
}
}
return result;
}
private static class PassThroughAttributesMap extends ConcurrentHashMap implements Serializable {
@Override
public Object put(String key, Object value) {
if (null == key || null == value) {
throw new NullPointerException();
}
validateKey(key);
return super.put(key, value);
}
@Override
public Object putIfAbsent(String key, Object value) {
if (null == key || null == value) {
throw new NullPointerException();
}
validateKey(key);
return super.putIfAbsent(key, value);
}
private void validateKey(Object key) {
if (!(key instanceof String) || (key instanceof ValueExpression) || !(key instanceof Serializable)) {
throw new IllegalArgumentException();
}
}
}
// ---------------------------------------------------------------- Bindings
/**
* {@inheritDoc}
*
* @throws NullPointerException {@inheritDoc}
* @deprecated This has been replaced by {@link #getValueExpression}.
*/
public ValueBinding getValueBinding(String name) {
if (name == null) {
throw new NullPointerException();
}
ValueBinding result = null;
ValueExpression ve;
if (null != (ve = getValueExpression(name))) {
// if the ValueExpression is an instance of our private
// wrapper class.
if (ve.getClass().equals(ValueExpressionValueBindingAdapter.class)) {
result = ((ValueExpressionValueBindingAdapter) ve).getWrapped();
} else {
// otherwise, this is a real ValueExpression. Wrap it
// in a ValueBinding.
result = new ValueBindingValueExpressionAdapter(ve);
}
}
return result;
}
/**
* {@inheritDoc}
*
* @throws IllegalArgumentException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @deprecated This has been replaced by {@link #setValueExpression}.
*/
public void setValueBinding(String name, ValueBinding binding) {
if (name == null) {
throw new NullPointerException();
}
if (binding != null) {
ValueExpressionValueBindingAdapter adapter =
new ValueExpressionValueBindingAdapter(binding);
setValueExpression(name, adapter);
} else {
setValueExpression(name, null);
}
}
// -------------------------------------------------------------- Properties
/**
* The assigned client identifier for this component.
*/
private String clientId = null;
/**
* @throws NullPointerException {@inheritDoc}
*/
public String getClientId(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// if the clientId is not yet set
if (this.clientId == null) {
UIComponent namingContainerAncestor =
this.getNamingContainerAncestor();
UIComponent parent = namingContainerAncestor;
String parentId = null;
// give the parent the opportunity to first
// grab a unique clientId
if (parent != null) {
parentId = parent.getContainerClientId(context);
}
// now resolve our own client id
this.clientId = getId();
if (this.clientId == null) {
String generatedId;
if (null != namingContainerAncestor &&
namingContainerAncestor instanceof UniqueIdVendor) {
generatedId = ((UniqueIdVendor)namingContainerAncestor).createUniqueId(context, null);
}
else {
generatedId = context.getViewRoot().createUniqueId();
}
setId(generatedId);
this.clientId = getId();
}
if (parentId != null) {
StringBuilder idBuilder =
new StringBuilder(parentId.length()
+ 1
+ this.clientId.length());
this.clientId = idBuilder.append(parentId)
.append(UINamingContainer.getSeparatorChar(context))
.append(this.clientId).toString();
}
// allow the renderer to convert the clientId
Renderer renderer = this.getRenderer(context);
if (renderer != null) {
this.clientId = renderer.convertClientId(context, this.clientId);
}
}
return this.clientId;
}
/**
* The component identifier for this component.
*/
private String id = null;
public String getId() {
return (id);
}
private UIComponent getNamingContainerAncestor() {
UIComponent namingContainer = this.getParent();
while (namingContainer != null) {
if (namingContainer instanceof NamingContainer) {
return namingContainer;
}
namingContainer = namingContainer.getParent();
}
return null;
}
/**
* @throws IllegalArgumentException {@inheritDoc}
* @throws IllegalStateException {@inheritDoc}
*/
public void setId(String id) {
// if the current ID is not null, and the passed
// argument is the same, no need to validate it
// as it has already been validated.
if (this.id == null || !(this.id.equals(id))) {
validateId(id);
this.id = id;
}
this.clientId = null; // Erase any cached value
}
/**
* The parent component for this component.
*/
private UIComponent parent = null;
public UIComponent getParent() {
return (this.parent);
}
public void setParent(UIComponent parent) {
if (parent == null) {
if (this.parent != null) {
doPreRemoveProcessing(FacesContext.getCurrentInstance(), this);
this.parent = parent;
}
compositeParent = null;
} else {
this.parent = parent;
if (this.getAttributes().get(ADDED) == null) {
// add an attribute to this component here to indiciate that
// it's being processed. If we don't do this, and the component
// is re-parented, the events could fire again in certain cases
// and cause a stack overflow.
this.getAttributes().put(ADDED, Boolean.TRUE);
doPostAddProcessing(FacesContext.getCurrentInstance(), this);
// remove the attribute once we've returned from the event
// processing.
this.getAttributes().remove(ADDED);
}
}
}
public boolean isRendered() {
return Boolean.valueOf(getStateHelper().eval(PropertyKeys.rendered, Boolean.TRUE).toString());
}
public void setRendered(boolean rendered) {
getStateHelper().put(PropertyKeys.rendered, rendered);
}
public String getRendererType() {
return (String) getStateHelper().eval(PropertyKeys.rendererType);
}
public void setRendererType(String rendererType) {
getStateHelper().put(PropertyKeys.rendererType, rendererType);
}
public boolean getRendersChildren() {
boolean result = false;
Renderer renderer;
if (getRendererType() != null) {
if (null !=
(renderer = getRenderer(getFacesContext()))) {
result = renderer.getRendersChildren();
}
}
return result;
}
// ------------------------------------------------- Tree Management Methods
/*
* The List
containing our child components.
*/
private List children = null;
public List getChildren() {
if (children == null) {
children = new ChildrenList(this);
}
return (children);
}
// Do not allocate the children List to answer this question
public int getChildCount() {
if (children != null) {
return (children.size());
} else {
return (0);
}
}
/**
* If the specified {@link UIComponent} has a non-null parent,
* remove it as a child or facet (as appropriate) of that parent.
* As a result, the parent
property will always be
* null
when this method returns.
*
* @param component {@link UIComponent} to have any parent erased
*/
private static void eraseParent(UIComponent component) {
UIComponent parent = component.getParent();
if (parent == null) {
return;
}
if (parent.getChildCount() > 0) {
List children = parent.getChildren();
int index = children.indexOf(component);
if (index >= 0) {
children.remove(index);
return;
}
}
if (parent.getFacetCount() > 0) {
Map facets = parent.getFacets();
Iterator entries = facets.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
//noinspection ObjectEquality
if (entry.getValue() == component) {
entries.remove();
return;
}
}
}
// Throw an exception for the "cannot happen" case
throw new IllegalStateException("Parent was not null, " +
"but this component not related");
}
/**
* Throw IllegalArgumentException
if the specified
* component identifier is non-null
and not
* syntactically valid.
*
* @param id The component identifier to test
*/
private static void validateId(String id) {
if (id == null) {
return;
}
int n = id.length();
if (n < 1) {
throw new IllegalArgumentException("Empty id attribute is not allowed");
}
for (int i = 0; i < n; i++) {
char c = id.charAt(i);
if (i == 0) {
if (!Character.isLetter(c) && (c != '_')) {
throw new IllegalArgumentException(id);
}
} else {
if (!Character.isLetter(c) &&
!Character.isDigit(c) &&
(c != '-') && (c != '_')) {
throw new IllegalArgumentException(id);
}
}
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public UIComponent findComponent(String expr) {
if (expr == null) {
throw new NullPointerException();
}
FacesContext ctx = FacesContext.getCurrentInstance();
final char sepChar = UINamingContainer.getSeparatorChar(ctx);
final String SEPARATOR_STRING = String.valueOf(sepChar);
if (expr.length() == 0) {
// if an empty value is provided, fail fast.
throw new IllegalArgumentException("\"\"");
}
// Identify the base component from which we will perform our search
UIComponent base = this;
if (expr.charAt(0) == sepChar) {
// Absolute searches start at the root of the tree
while (base.getParent() != null) {
base = base.getParent();
}
// Treat remainder of the expression as relative
expr = expr.substring(1);
} else if (!(base instanceof NamingContainer)) {
// Relative expressions start at the closest NamingContainer or root
while (base.getParent() != null) {
if (base instanceof NamingContainer) {
break;
}
base = base.getParent();
}
}
// Evaluate the search expression (now guaranteed to be relative)
UIComponent result = null;
String[] segments = expr.split(SEPARATOR_STRING);
for (int i = 0, length = (segments.length - 1);
i < segments.length;
i++, length--) {
result = findComponent(base, segments[i], (i == 0));
// the first element of the expression may match base.id
// (vs. a child if of base)
if (i == 0 && result == null &&
segments[i].equals(base.getId())) {
result = base;
}
if (result != null && (!(result instanceof NamingContainer)) && length > 0) {
throw new IllegalArgumentException(segments[i]);
}
if (result == null) {
break;
}
base = result;
}
// Return the final result of our search
return (result);
}
/**
* Return the {@link UIComponent} (if any) with the specified
* id
, searching recursively starting at the specified
* base
, and examining the base component itself, followed
* by examining all the base component's facets and children (unless
* the base component is a {@link NamingContainer}, in which case the
* recursive scan is skipped.
*
* @param base Base {@link UIComponent} from which to search
* @param id Component identifier to be matched
*/
private static UIComponent findComponent(UIComponent base,
String id,
boolean checkId) {
if (checkId && id.equals(base.getId())) {
return base;
}
// Search through our facets and children
UIComponent result = null;
for (Iterator i = base.getFacetsAndChildren(); i.hasNext();) {
UIComponent kid = (UIComponent) i.next();
if (!(kid instanceof NamingContainer)) {
if (checkId && id.equals(kid.getId())) {
result = kid;
break;
}
result = findComponent(kid, id, true);
if (result != null) {
break;
}
} else if (id.equals(kid.getId())) {
result = kid;
break;
}
}
return (result);
}
/**
* {@inheritDoc}
*
* @throws NullPointerException {@inheritDoc}
* @throws FacesException {@inheritDoc}
* @since 1.2
*/
public boolean invokeOnComponent(FacesContext context, String clientId,
ContextCallback callback)
throws FacesException {
return super.invokeOnComponent(context, clientId, callback);
}
// ------------------------------------------------ Facet Management Methods
/*
* The Map
containing our related facet components.
*/
private Map facets = null;
public Map getFacets() {
if (facets == null) {
facets = new FacetsMap(this);
}
return (facets);
}
// Do not allocate the children List to answer this question
public int getFacetCount() {
if (facets != null) {
return (facets.size());
} else {
return (0);
}
}
// Do not allocate the facets Map to answer this question
public UIComponent getFacet(String name) {
if (facets != null) {
return (facets.get(name));
} else {
return (null);
}
}
public Iterator getFacetsAndChildren() {
Iterator result;
int childCount = this.getChildCount(),
facetCount = this.getFacetCount();
// If there are neither facets nor children
if (0 == childCount && 0 == facetCount) {
result = EMPTY_ITERATOR;
}
// If there are only facets and no children
else if (0 == childCount) {
Collection unmodifiable =
Collections.unmodifiableCollection(getFacets().values());
result = unmodifiable.iterator();
}
// If there are only children and no facets
else if (0 == facetCount) {
List unmodifiable =
Collections.unmodifiableList(getChildren());
result = unmodifiable.iterator();
}
// If there are both children and facets
else {
result = new FacetsAndChildrenIterator(this);
}
return result;
}
// -------------------------------------------- Lifecycle Processing Methods
/**
* @throws AbortProcessingException {@inheritDoc}
* @throws IllegalStateException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void broadcast(FacesEvent event)
throws AbortProcessingException {
if (event == null) {
throw new NullPointerException();
}
if (event instanceof BehaviorEvent) {
BehaviorEvent behaviorEvent = (BehaviorEvent) event;
Behavior behavior = behaviorEvent.getBehavior();
behavior.broadcast(behaviorEvent);
}
if (listeners == null) {
return;
}
for (FacesListener listener : listeners.asArray(FacesListener.class)) {
if (event.isAppropriateListener(listener)) {
event.processListener(listener);
}
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public void decode(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
String rendererType = getRendererType();
if (rendererType != null) {
Renderer renderer = this.getRenderer(context);
if (renderer != null) {
renderer.decode(context, this);
} else {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Can't get Renderer for type " + rendererType);
}
}
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public void encodeBegin(FacesContext context) throws IOException {
if (context == null) {
throw new NullPointerException();
}
pushComponentToEL(context, null);
if (!isRendered()) {
return;
}
context.getApplication().publishEvent(context,
PreRenderComponentEvent.class,
this);
String rendererType = getRendererType();
if (rendererType != null) {
Renderer renderer = this.getRenderer(context);
if (renderer != null) {
renderer.encodeBegin(context, this);
} else {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Can't get Renderer for type " + rendererType);
}
}
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public void encodeChildren(FacesContext context) throws IOException {
if (context == null) {
throw new NullPointerException();
}
if (!isRendered()) {
return;
}
String rendererType = getRendererType();
if (rendererType != null) {
Renderer renderer = this.getRenderer(context);
if (renderer != null) {
renderer.encodeChildren(context, this);
}
// We've already logged for this component
} else {
if (getChildCount() > 0) {
for (UIComponent child : getChildren()) {
child.encodeAll(context);
}
}
}
}
/**
* @throws IOException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void encodeEnd(FacesContext context) throws IOException {
if (context == null) {
throw new NullPointerException();
}
if (!isRendered()) {
popComponentFromEL(context);
return;
}
String rendererType = getRendererType();
if (rendererType != null) {
Renderer renderer = this.getRenderer(context);
if (renderer != null) {
renderer.encodeEnd(context, this);
}
}
popComponentFromEL(context);
}
// -------------------------------------------------- Event Listener Methods
private AttachedObjectListHolder listeners;
/**
* Add the specified {@link FacesListener} to the set of listeners
* registered to receive event notifications from this {@link UIComponent}.
* It is expected that {@link UIComponent} classes acting as event sources
* will have corresponding typesafe APIs for registering listeners of the
* required type, and the implementation of those registration methods
* will delegate to this method. For example:
*
* public class FooEvent extends FacesEvent {
* ...
* protected boolean isAppropriateListener(FacesListener listener) {
* return (listener instanceof FooListener);
* }
* protected void processListener(FacesListener listener) {
* ((FooListener) listener).processFoo(this);
* }
* ...
* }
*
* public interface FooListener extends FacesListener {
* public void processFoo(FooEvent event);
* }
*
* public class FooComponent extends UIComponentBase {
* ...
* public void addFooListener(FooListener listener) {
* addFacesListener(listener);
* }
* public void removeFooListener(FooListener listener) {
* removeFacesListener(listener);
* }
* ...
* }
*
*
* @param listener The {@link FacesListener} to be registered
* @throws NullPointerException if listener
* is null
*/
protected void addFacesListener(FacesListener listener) {
if (listener == null) {
throw new NullPointerException();
}
if (listeners == null) {
listeners = new AttachedObjectListHolder();
}
listeners.add(listener);
}
/**
* @throws IllegalArgumentException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
protected FacesListener[] getFacesListeners(Class clazz) {
if (clazz == null) {
throw new NullPointerException();
}
if (!FacesListener.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException();
}
if (this.listeners == null) {
return (FacesListener[]) Array.newInstance(clazz, 0);
}
FacesListener[] listeners = this.listeners.asArray(FacesListener.class);
if (listeners.length == 0) {
return (FacesListener[]) Array.newInstance(clazz, 0);
}
List results = new ArrayList(listeners.length);
for (FacesListener listener : listeners) {
if (((Class>) clazz).isAssignableFrom(listener.getClass())) {
results.add(listener);
}
}
return (results.toArray
((FacesListener[]) Array.newInstance(clazz,
results.size())));
}
/**
* Remove the specified {@link FacesListener} from the set of listeners
* registered to receive event notifications from this {@link UIComponent}.
*
* @param listener The {@link FacesListener} to be deregistered
* @throws NullPointerException if listener
* is null
*/
protected void removeFacesListener(FacesListener listener) {
if (listener == null) {
throw new NullPointerException();
}
if (listeners != null) {
listeners.remove(listener);
}
}
/**
* @throws IllegalStateException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void queueEvent(FacesEvent event) {
if (event == null) {
throw new NullPointerException();
}
UIComponent parent = getParent();
if (parent == null) {
throw new IllegalStateException();
} else {
parent.queueEvent(event);
}
}
/**
*
Install the listener instance
* referenced by argument componentListener
as a
* listener for events of type eventClass
originating
* from this specific instance of UIComponent
. The
* default implementation creates an inner {@link
* SystemEventListener} instance that wraps argument
* componentListener
as the listener
* argument. This inner class must call through to the argument
* componentListener
in its implementation of {@link
* SystemEventListener#processEvent} and its implementation of
* {@link SystemEventListener#isListenerForSource} must return
* true if the instance class of this UIComponent
is
* assignable from the argument to
* isListenerForSource
.
*
* @param eventClass the Class
of event for which
* listener
must be fired.
* @param componentListener the implementation of {@link
* javax.faces.event.ComponentSystemEventListener} whose {@link
* javax.faces.event.ComponentSystemEventListener#processEvent} method must be called
* when events of type facesEventClass
are fired.
*
* @throws NullPointerException
if any of the
* arguments are null
.
*
* @since 2.1
*/
public void subscribeToEvent(Class extends SystemEvent> eventClass,
ComponentSystemEventListener componentListener) {
if (eventClass == null) {
throw new NullPointerException();
}
if (componentListener == null) {
throw new NullPointerException();
}
if (initialStateMarked()) {
initialState = false;
}
if (null == listenersByEventClass) {
listenersByEventClass = new HashMap,
List>(3, 1.0f);
}
SystemEventListener facesLifecycleListener =
new ComponentSystemEventListenerAdapter(componentListener, this);
List listenersForEventClass =
listenersByEventClass.get(eventClass);
if (listenersForEventClass == null) {
listenersForEventClass = new ArrayList(3);
listenersByEventClass.put(eventClass, listenersForEventClass);
}
if (!listenersForEventClass.contains(facesLifecycleListener)) {
listenersForEventClass.add(facesLifecycleListener);
}
}
/**
* Remove the listener instance
* referenced by argument componentListener
as a
* listener for events of type eventClass
* originating from this specific instance of
* UIComponent
. When doing the comparison to
* determine if an existing listener is equal to the argument
* componentListener
(and thus must be removed),
* the equals()
method on the existing
* listener must be invoked, passing the argument
* componentListener
, rather than the other way
* around.
*
* @param eventClass the Class
of event for which
* listener
must be removed.
* @param componentListener the implementation of {@link
* ComponentSystemEventListener} whose {@link
* ComponentSystemEventListener#processEvent} method must no longer be called
* when events of type eventClass
are fired.
*
* @throws NullPointerException
if any of the
* arguments are null
.
*
* @since 2.1
*/
public void unsubscribeFromEvent(Class extends SystemEvent> eventClass,
ComponentSystemEventListener componentListener) {
if (eventClass == null) {
throw new NullPointerException();
}
if (componentListener == null) {
throw new NullPointerException();
}
List listeners =
getListenersForEventClass(eventClass);
if (listeners != null && !listeners.isEmpty()) {
for (Iterator i = listeners.iterator(); i.hasNext();) {
SystemEventListener item = i.next();
ComponentSystemEventListenerAdapter csla =
(ComponentSystemEventListenerAdapter) item;
ComponentSystemEventListener l = csla.getWrapped();
if (l.equals(componentListener)) {
i.remove();
break;
}
}
}
}
/**
* Return the
* SystemEventListener
instances registered on this
* UIComponent
instance that are interested in events
* of type eventClass
.
*
* @param eventClass the Class
of event for which the
* listeners must be returned.
* @throws NullPointerException if argument eventClass
is null
.
*
* @since 2.1
*/
public List getListenersForEventClass(Class extends SystemEvent> eventClass) {
if (eventClass == null) {
throw new NullPointerException();
}
List result = null;
if (listenersByEventClass != null) {
result = listenersByEventClass.get(eventClass);
}
return result;
}
// ------------------------------------------------ Lifecycle Phase Handlers
/**
* @throws NullPointerException {@inheritDoc}
*/
public void processDecodes(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, null);
// Process all facets and children of this component
Iterator kids = getFacetsAndChildren();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
kid.processDecodes(context);
}
// Process this component itself
try {
decode(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
} finally {
popComponentFromEL(context);
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public void processValidators(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, null);
Application app = context.getApplication();
app.publishEvent(context, PreValidateEvent.class, this);
// Process all the facets and children of this component
Iterator kids = getFacetsAndChildren();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
kid.processValidators(context);
}
app.publishEvent(context, PostValidateEvent.class, this);
popComponentFromEL(context);
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public void processUpdates(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, null);
// Process all facets and children of this component
Iterator kids = getFacetsAndChildren();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
kid.processUpdates(context);
}
popComponentFromEL(context);
}
private static final int MY_STATE = 0;
private static final int CHILD_STATE = 1;
/**
* @throws NullPointerException {@inheritDoc}
*/
public Object processSaveState(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
if (this.isTransient()) {
return null;
}
Object[] stateStruct = new Object[2];
Object[] childState = EMPTY_ARRAY;
pushComponentToEL(context, null);
// Process this component itself
stateStruct[MY_STATE] = saveState(context);
// determine if we have any children to store
int count = this.getChildCount() + this.getFacetCount();
if (count > 0) {
// this arraylist will store state
List