blob: f02c74bdae27ba0e7fd7ee4d704bb2db1a05c951 [file] [log] [blame]
/*
* 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 javax.faces.component;
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 javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.behavior.Behavior;
import javax.faces.component.behavior.ClientBehavior;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.BehaviorEvent;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.event.PostAddToViewEvent;
import javax.faces.event.PostValidateEvent;
import javax.faces.event.PreRemoveFromViewEvent;
import javax.faces.event.PreRenderComponentEvent;
import javax.faces.event.PreValidateEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.render.RenderKit;
import javax.faces.render.Renderer;
import javax.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 javax.faces.event.PhaseId;
/**
* Standard implementation of the UIComponent base class; all standard JSF components extend this class.
* <p>
* <i>Disclaimer</i>: The official definition for the behaviour of this class is the JSF 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 <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a> for
* more.
*/
@JSFComponent(type = "javax.faces.ComponentBase", family = "javax.faces.ComponentBase",
desc = "base component when all components must inherit",
tagClass = "javax.faces.webapp.UIComponentELTag", configExcluded = true)
@JSFJspProperty(name = "binding", returnType = "javax.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
= "javax.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<UIComponent> _childrenList = null;
private Map<String, UIComponent> _facetMap = null;
private _DeltaList<FacesListener> _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<String, List<ClientBehavior>> _behaviorsMap = null;
private transient Map<String, List<ClientBehavior>> _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.
* <p>
* The id must start with an underscore if it is generated by the JSF framework, and must <i>not</i> start with an
* underscore if it has been specified by the user (eg in a JSP tag).
* <p>
* The first character of the id must be an underscore or letter. Following characters may be letters, digits,
* underscores or dashes.
* <p>
* Null is allowed as a parameter, and will reset the id to null.
* <p>
* 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;
}
/**
* <p>Set the parent <code>UIComponent</code> of this
* <code>UIComponent</code>.</p>
*
* @param parent The new parent, or <code>null</code> 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<UIComponent> 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<String> 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);
}
// Normally have client only 1 client behaviour per event name, so size 2 must be sufficient:
List<ClientBehavior> behaviorsForEvent = _behaviorsMap.computeIfAbsent(eventName,
k -> new _DeltaList<>(2));
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.
* <p>
* This method does not propagate the event up to parent components, ie listeners attached to parent components
* don't automatically get called.
* <p>
* 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.
* <p>
* 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
{
if (event == null)
{
throw new NullPointerException("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 (javax.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<String, List<ClientBehavior>> entry : _behaviorsMap.entrySet())
{
((PartialStateHolder) entry.getValue()).clearInitialState();
}
}
if (_systemEventListenerClassMap != null)
{
for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener>> 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)
{
if (context == null)
{
throw new NullPointerException("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
{
if (context == null)
{
throw new NullPointerException();
}
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
{
if (context == null)
{
throw new NullPointerException("context");
}
try
{
setCachedFacesContext(context);
// Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.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
{
if (context == null)
{
throw new NullPointerException("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(javax.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
{
if (context == null)
{
throw new NullPointerException("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(javax.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.
* <p>
* 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.
* <p>
* The base node at which the search starts is determined as follows:
* <ul>
* <li>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).
* <li>Otherwise, if this component is a NamingContainer then the search starts with this component.
* <li>Otherwise, the search starts from the nearest ancestor NamingContainer (or the root component if there is no
* NamingContainer ancestor).
* </ul>
*
* @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)
{
if (expr == null)
{
throw new NullPointerException("expr");
}
if (expr.length() == 0)
{
return null;
}
char separatorChar = getFacesContext().getNamingContainerSeparatorChar();
UIComponent findBase;
if (expr.charAt(0) == separatorChar)
{
findBase = _ComponentUtils.getRootComponent(this);
expr = expr.substring(1);
}
else
{
if (this instanceof NamingContainer)
{
findBase = this;
}
else
{
findBase = _ComponentUtils.findParentNamingContainer(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.
* <p>
* When writing to the returned map:
* <ul>
* <li>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.
* <li>Otherwise the key/value pair is stored in a map associated with the component.
* </ul>
* Note that value-bindings are <i>not</i> 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 <i>not</i> evaluate a binding of the same name; they just store the provided value directly on the
* component.
* <p>
* When reading from the returned map:
* <ul>
* <li>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.
* <li>If the attribute map associated with the component has an entry with the specified key, then that is
* returned.
* <li>If this component has a value-binding for the specified key, then the value-binding is evaluated to fetch the
* value.
* <li>Otherwise, null is returned.
* </ul>
* 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.
* <p>
* 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.
* <p>
* 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<String, Object> getAttributes()
{
if (_attributesMap == null)
{
_attributesMap = new _ComponentAttributesMap(this);
}
return _attributesMap;
}
@Override
public Map<String, Object> 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.
* <p>
* 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.
* <p>
* The list object returned has some non-standard behaviour:
* <ul>
* <li>The list is type-checked; only UIComponent objects can be added.
* <li>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.
* <li>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.
* </ul>
*/
@Override
public List<UIComponent> getChildren()
{
if (_childrenList == null)
{
_childrenList = new _ComponentChildrenList(this);
}
return _childrenList;
}
/**
*
* @return
*
* @since 2.0
*/
public Map<String,List<ClientBehavior>> 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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)
{
if (context == null)
{
throw new NullPointerException("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.closest(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.findParentNamingContainer(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<String> 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<String, UIComponent> getFacets()
{
if (_facetMap == null)
{
_facetMap = new _ComponentFacetMap<>(this);
}
return _facetMap;
}
@Override
public Iterator<UIComponent> 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.<UIComponent>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 JSF spec section 8 Rendering Model,
// this part is essential to implement "delegated implementation" pattern,
// so we can't do this optimization here. Instead, JSF developers could prevent
// this evaluation overriding this method directly.
if (_rendererType != null)
{
return _rendererType;
}
ValueExpression expression = getValueExpression("rendererType");
if (expression != null)
{
return (String) 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:
* <ul>
* <li>This component's encodeBegin method will only be called after all the child components have been created and
* added to this component. <li>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. <li>No rendering methods
* will be called automatically on child components; this component is required to invoke the
* encodeBegin/encodeEnd/etc on them itself.
* </ul>
*/
@Override
public boolean getRendersChildren()
{
Renderer renderer = getRenderer(getFacesContext());
return renderer != null ? renderer.getRendersChildren() : false;
}
/**
* <code>invokeOnComponent</code> must be implemented in <code>UIComponentBase</code> 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<String, List<ClientBehavior>> entry : _behaviorsMap.entrySet())
{
((PartialStateHolder) entry.getValue()).markInitialState();
}
}
if (_systemEventListenerClassMap != null)
{
for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener>> entry :
_systemEventListenerClassMap.entrySet())
{
((PartialStateHolder) entry.getValue()).markInitialState();
}
}
}
@Override
protected void addFacesListener(FacesListener listener)
{
if (listener == null)
{
throw new NullPointerException("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)
{
if (clazz == null)
{
throw new NullPointerException("Class is null");
}
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<FacesListener> lst = null;
// perf: _facesListeners is RandomAccess instance (javax.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)
{
if (context == null)
{
throw new NullPointerException("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)
{
if (listener == null)
{
throw new NullPointerException("listener is null");
}
if (_facesListeners != null)
{
_facesListeners.remove(listener);
}
}
@Override
public void queueEvent(FacesEvent event)
{
if (event == null)
{
throw new NullPointerException("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(javax.faces.context.FacesContext, javax.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(javax.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(javax.faces.context.FacesContext, javax.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.
* <p>
* 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(javax.faces.context.FacesContext, javax.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(javax.faces.context.FacesContext)
popComponentFromEL(context);
setCachedFacesContext(null);
}
}
@Override
public Object processSaveState(FacesContext context)
{
if (context == null)
{
throw new NullPointerException("context");
}
if (isTransient())
{
// consult the transient property of this component. If true, just return null.
return null;
}
// Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
pushComponentToEL(context, this);
Map<String, Object> facetMap;
List<Object> childrenList;
Object savedState;
try
{
facetMap = null;
int facetCount = getFacetCount();
if (facetCount > 0)
{
// Call the processSaveState() method of all facets and children of this UIComponent in the order
// determined by a call to getFacetsAndChildren(), skipping children and facets that are transient.
// To improve speed and robustness, the facets and children processing is splited to maintain the
// facet --> state coherence based on the facet's name
for (Map.Entry<String, UIComponent> entry : getFacets().entrySet())
{
UIComponent component = entry.getValue();
if (!component.isTransient())
{
if (facetMap == null)
{
facetMap = new HashMap<>(facetCount, 1);
}
facetMap.put(entry.getKey(), component.processSaveState(context));
}
}
}
childrenList = null;
int childCount = getChildCount();
if (childCount > 0)
{
// Call the processSaveState() method of all facets and children of this UIComponent in the order
// determined by a call to getFacetsAndChildren(), skipping children and facets that are transient.
// To improve speed and robustness, the facets and children processing is splited to maintain the
// facet --> state coherence based on the facet's name
for (int i = 0; i < childCount; i++)
{
UIComponent child = getChildren().get(i);
if (!child.isTransient())
{
if (childrenList == null)
{
childrenList = new ArrayList<>(childCount);
}
Object childState = child.processSaveState(context);
if (childState != null)
{ // FIXME: Isn't that check dangerous for restoration since the child isn't marked transient?
childrenList.add(childState);
}
}
}
}
// Call the saveState() method of this component.
savedState = saveState(context);
}
finally
{
// Ensure that UIComponent.popComponentFromEL(javax.faces.context.FacesContext) is called
// correctly after each child or facet.
popComponentFromEL(context);
}
// Encapsulate the child state and your state into a Serializable Object and return it.
return new Object[] { savedState, facetMap, childrenList };
}
@SuppressWarnings("unchecked")
@Override
public void processRestoreState(FacesContext context, Object state)
{
if (context == null)
{
throw new NullPointerException("context");
}
Object[] stateValues = (Object[]) state;
try
{
// Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
pushComponentToEL(context, this);
// Call the restoreState() method of this component.
restoreState(context, stateValues[0]);
Map<String, Object> facetMap = (Map<String, Object>) stateValues[1];
if (facetMap != null && getFacetCount() > 0)
{
// Call the processRestoreState() method of all facets and children of this UIComponent in the order
// determined by a call to getFacetsAndChildren().
// To improve speed and robustness, the facets and children processing is splited to maintain the
// facet --> state coherence based on the facet's name
for (Map.Entry<String, UIComponent> entry : getFacets().entrySet())
{
Object facetState = facetMap.get(entry.getKey());
if (facetState != null)
{
entry.getValue().processRestoreState(context, facetState);
}
else
{
context.getExternalContext().log("No state found to restore facet " + entry.getKey());
}
}
}
List<Object> childrenList = (List<Object>) stateValues[2];
if (childrenList != null && getChildCount() > 0)
{
// Call the processRestoreState() method of all facets and children of this UIComponent in the order
// determined by a call to getFacetsAndChildren().
// To improve speed and robustness, the facets and children processing is splited to maintain the
// facet --> state coherence based on the facet's name
int idx = 0;
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
if (!child.isTransient())
{
Object childState = childrenList.get(idx++);
if (childState != null)
{
child.processRestoreState(context, childState);
}
else
{
context.getExternalContext().log("No state found to restore child of component " + getId());
}
}
}
}
}
finally
{
// After returning from the processRestoreState() method on a child or facet, call
// UIComponent.popComponentFromEL(javax.faces.context.FacesContext)
popComponentFromEL(context);
}
}
/**
* Gets the Location of the given UIComponent from its attribute map.
* @param component
* @return
*/
private String getComponentLocation(UIComponent component)
{
Location location = (Location) component.getAttributes().get(UIComponent.VIEW_LOCATION_KEY);
return location == null ? null : location.toString();
}
@Override
public void setTransient(boolean transientFlag)
{
_transient = transientFlag;
}
/**
* Serializes objects which are "attached" to this component but which are not UIComponent children of it. Examples
* are validator and listener objects. To be precise, it returns an object which implements java.io.Serializable,
* and which when serialized will persist the state of the provided object.
* <p>
* If the attachedObject is a List then every object in the list is saved via a call to this method, and the
* returned wrapper object contains a List object.
* <p>
* If the object implements StateHolder then the object's saveState is called immediately, and a wrapper is returned
* which contains both this saved state and the original class name. However in the case where the
* StateHolder.isTransient method returns true, null is returned instead.
* <p>
* If the object implements java.io.Serializable then the object is simply returned immediately; standard java
* serialization will later be used to store this object.
* <p>
* In all other cases, a wrapper is returned which simply stores the type of the provided object. When deserialized,
* a default instance of that type will be recreated.
*/
public static Object saveAttachedState(FacesContext context, Object attachedObject)
{
if (context == null)
{
throw new NullPointerException ("context");
}
if (attachedObject == null)
{
return null;
}
// StateHolder interface should take precedence over
// List children
if (attachedObject instanceof StateHolder)
{
StateHolder holder = (StateHolder) attachedObject;
if (holder.isTransient())
{
return null;
}
return new _AttachedStateWrapper(attachedObject.getClass(), holder.saveState(context));
}
else if (attachedObject instanceof Collection)
{
if (ArrayList.class.equals(attachedObject.getClass()))
{
ArrayList<?> list = (ArrayList<?>) attachedObject;
int size = list.size();
List<Object> lst = new ArrayList<Object>(size);
for (int i = 0; i < size; i++)
{
Object item = list.get(i);
if (item != null)
{
lst.add(saveAttachedState(context, item));
}
}
return new _AttachedListStateWrapper(lst);
}
else
{
List<Object> lst = new ArrayList<>(((Collection<?>) attachedObject).size());
for (Object item : (Collection<?>) attachedObject)
{
if (item != null)
{
lst.add(saveAttachedState(context, item));
}
}
return new _AttachedCollectionStateWrapper(attachedObject.getClass(), lst);
}
}
else if (attachedObject instanceof Serializable)
{
return attachedObject;
}
else
{
return new _AttachedStateWrapper(attachedObject.getClass(), null);
}
}
public static Object restoreAttachedState(FacesContext context, Object stateObj) throws IllegalStateException
{
if (context == null)
{
throw new NullPointerException("context");
}
if (stateObj == null)
{
return null;
}
if (stateObj instanceof _AttachedListStateWrapper)
{
// perf: getWrappedStateList in _AttachedListStateWrapper is always ArrayList: see saveAttachedState
ArrayList<Object> lst = (ArrayList<Object>) ((_AttachedListStateWrapper) stateObj).getWrappedStateList();
List<Object> restoredList = new ArrayList<Object>(lst.size());
for (int i = 0, size = lst.size(); i < size; i++)
{
Object item = lst.get(i);
restoredList.add(restoreAttachedState(context, item));
}
return restoredList;
}
else if (stateObj instanceof _AttachedCollectionStateWrapper)
{
_AttachedCollectionStateWrapper wrappedState = (_AttachedCollectionStateWrapper) stateObj;
Class<?> clazz = wrappedState.getClazz();
List<Object> lst = wrappedState.getWrappedStateList();
Collection restoredList;
try
{
restoredList = (Collection) clazz.newInstance();
}
catch (InstantiationException e)
{
throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
+ " (missing no-args constructor?)", e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
for (int i = 0; i < lst.size(); i++)
{
Object item = lst.get(i);
restoredList.add(restoreAttachedState(context, item));
}
return restoredList;
}
else if (stateObj instanceof _AttachedStateWrapper)
{
Class<?> clazz = ((_AttachedStateWrapper) stateObj).getClazz();
Object restoredObject;
try
{
restoredObject = clazz.newInstance();
}
catch (InstantiationException e)
{
throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
+ " (missing no-args constructor?)", e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
if (restoredObject instanceof StateHolder)
{
_AttachedStateWrapper wrapper = (_AttachedStateWrapper) stateObj;
Object wrappedState = wrapper.getWrappedStateObject();
StateHolder holder = (StateHolder) restoredObject;
holder.restoreState(context, wrappedState);
}
return restoredObject;
}
else
{
return stateObj;
}
}
private static final int FULL_STATE_ARRAY_SIZE = 10;
/**
* Invoked after the render phase has completed, this method returns an object which can be passed to the
* restoreState of some other instance of UIComponentBase to reset that object's state to the same values as this
* object currently has.
*/
@Override
public Object saveState(FacesContext context)
{
if (context == null)
{
throw new NullPointerException ("context");
}
if (context.getViewRoot() != null)
{
if (context.getViewRoot().getResetSaveStateMode() == RESET_MODE_SOFT)
{
// Force FacesContext cleanup to prevent leak it.
setCachedFacesContext(null);
// Reset state to recalculate state first.
StateHelper stateHelper = getStateHelper(false);
if (stateHelper != null)
{
((_DeltaStateHelper)stateHelper).resetSoftState(context);
}
}
if (context.getViewRoot().getResetSaveStateMode() == RESET_MODE_HARD)
{
// Force FacesContext cleanup to prevent leak it.
setCachedFacesContext(null);
// Reset state to recalculate state first.
StateHelper stateHelper = getStateHelper(false);
if (stateHelper != null)
{
((_DeltaStateHelper)stateHelper).resetHardState(context);
}
}
}
if (initialStateMarked())
{
//Delta
//_id and _clientId was already restored from template
//and never changes during component life.
Object facesListenersSaved = saveFacesListenersList(context);
Object behaviorsMapSaved = saveBehaviorsMap(context);
Object systemEventListenerClassMapSaved = saveSystemEventListenerClassMap(context);
Object stateHelperSaved = null;
StateHelper stateHelper = getStateHelper(false);
if (stateHelper != null)
{
stateHelperSaved = stateHelper.saveState(context);
}
if (facesListenersSaved == null && stateHelperSaved == null &&
behaviorsMapSaved == null && systemEventListenerClassMapSaved == null &&
!((_capabilities & FLAG_IS_RENDERER_TYPE_SET) != 0))
{
return null;
}
Object transientState = null;
if (context.getCurrentPhaseId() != null &&
!PhaseId.RENDER_RESPONSE.equals(context.getCurrentPhaseId()))
{
transientState = saveTransientState(context);
}
if (transientState != null)
{
if ((_capabilities & FLAG_IS_RENDERER_TYPE_SET) != 0)
{
return new Object[] {facesListenersSaved, stateHelperSaved, behaviorsMapSaved,
systemEventListenerClassMapSaved, transientState,
_rendererType};
}
else
{
return new Object[] {facesListenersSaved, stateHelperSaved, behaviorsMapSaved,
systemEventListenerClassMapSaved, transientState};
}
}
else
{
if ((_capabilities & FLAG_IS_RENDERER_TYPE_SET) != 0)
{
return new Object[] {facesListenersSaved, stateHelperSaved, behaviorsMapSaved,
systemEventListenerClassMapSaved, null,
_rendererType};
}
else
{
return new Object[] {facesListenersSaved, stateHelperSaved, behaviorsMapSaved,
systemEventListenerClassMapSaved};
}
}
}
else
{
//Full
Object values[] = new Object[FULL_STATE_ARRAY_SIZE];
values[0] = saveFacesListenersList(context);
StateHelper stateHelper = getStateHelper(false);
if (stateHelper != null)
{
values[1] = stateHelper.saveState(context);
}
values[2] = saveBehaviorsMap(context);
values[3] = saveSystemEventListenerClassMap(context);
values[4] = _id;
values[5] = _clientId;
values[6] = _markCreated;
values[7] = _rendererType;
values[8] = _capabilities;
if (context.getCurrentPhaseId() != null &&
!PhaseId.RENDER_RESPONSE.equals(context.getCurrentPhaseId()))
{
values[9] = saveTransientState(context);
}
return values;
}
}
/**
* Invoked in the "restore view" phase, this initialises this object's members from the values saved previously into
* the provided state object.
* <p>
*
* @param state
* is an object previously returned by the saveState method of this class.
*/
@SuppressWarnings("unchecked")
@Override
public void restoreState(FacesContext context, Object state)
{
if (context == null)
{
throw new NullPointerException ("context");
}
if (state == null)
{
//Only happens if initialStateMarked return true
if (initialStateMarked())
{
return;
}
throw new NullPointerException ("state");
}
Object values[] = (Object[]) state;
if ( values.length == FULL_STATE_ARRAY_SIZE && initialStateMarked())
{
//Delta mode is active, but we are restoring a full state.
//we need to clear the initial state, to restore state without
//take into account delta.
clearInitialState();
}
if (values[0] instanceof _AttachedDeltaWrapper)
{
//Delta: check for null is not necessary since _facesListener field
//is only set once and never reset
((StateHolder)_facesListeners).restoreState(context,
((_AttachedDeltaWrapper) values[0]).getWrappedStateObject());
}
else if (values[0] != null || (values.length == FULL_STATE_ARRAY_SIZE))
{
//Full
_facesListeners = (_DeltaList<FacesListener>)
restoreAttachedState(context,values[0]);
}
// Note that if values[0] == null && initialStateMarked(),
// means delta is null, not that _facesListeners == null.
// We can do this here because _facesListeners instance once
// is created is never replaced or set to null.
getStateHelper().restoreState(context, values[1]);
if (values.length == FULL_STATE_ARRAY_SIZE)
{
_id = (String) values[4];
_clientId = (String) values[5];
_markCreated = (String) values[6];
_rendererType = (String) values[7];
_capabilities = (Integer) values[8];
}
else if (values.length == 6)
{
restoreTransientState(context, values[4]);
_rendererType = (String) values[5];
_capabilities |= FLAG_IS_RENDERER_TYPE_SET;
}
else if (values.length == 5)
{
restoreTransientState(context, values[4]);
}
// rendererType needs to be restored before SystemEventListener,
// otherwise UIComponent.getCurrentComponent(context).getRenderer(context)
// will not work correctly
if (values.length == FULL_STATE_ARRAY_SIZE)
{
//Full restore
restoreFullBehaviorsMap(context, values[2]);
restoreFullSystemEventListenerClassMap(context, values[3]);
restoreTransientState(context, values[9]);
}
else
{
//Delta restore
restoreDeltaBehaviorsMap(context, values[2]);
restoreDeltaSystemEventListenerClassMap(context, values[3]);
}
}
private Object saveFacesListenersList(FacesContext facesContext)
{
PartialStateHolder holder = (PartialStateHolder) _facesListeners;
if (initialStateMarked() && _facesListeners != null && holder.initialStateMarked())
{
Object attachedState = holder.saveState(facesContext);
if (attachedState != null)
{
return new _AttachedDeltaWrapper(_facesListeners.getClass(), attachedState);
}
//_facesListeners instances once is created never changes, we can return null
return null;
}
else
{
return saveAttachedState(facesContext,_facesListeners);
}
}
@SuppressWarnings("unchecked")
private void restoreFullBehaviorsMap(FacesContext facesContext, Object stateObj)
{
if (stateObj != null)
{
Map<String, Object> stateMap = (Map<String, Object>) stateObj;
_behaviorsMap = new HashMap<>(stateMap.size(), 1f);
_unmodifiableBehaviorsMap = null;
for (Map.Entry<String, Object> entry : stateMap.entrySet())
{
_behaviorsMap.put(entry.getKey(),
(List<ClientBehavior>) restoreAttachedState(facesContext, entry.getValue()));
}
}
else
{
_behaviorsMap = null;
_unmodifiableBehaviorsMap = null;
}
}
@SuppressWarnings("unchecked")
private void restoreDeltaBehaviorsMap(FacesContext facesContext, Object stateObj)
{
if (stateObj != null)
{
_unmodifiableBehaviorsMap = null;
Map<String, Object> stateMap = (Map<String, Object>) stateObj;
if (_behaviorsMap == null)
{
_behaviorsMap = new HashMap<>(stateMap.size(), 1f);
}
for (Map.Entry<String, Object> entry : stateMap.entrySet())
{
Object savedObject = entry.getValue();
if (savedObject instanceof _AttachedDeltaWrapper)
{
StateHolder holderList = (StateHolder) _behaviorsMap.get(entry.getKey());
holderList.restoreState(facesContext,
((_AttachedDeltaWrapper) savedObject).getWrappedStateObject());
}
else
{
_behaviorsMap.put(entry.getKey(),
(List<ClientBehavior>) restoreAttachedState(facesContext, savedObject));
}
}
}
}
private Object saveBehaviorsMap(FacesContext facesContext)
{
if (_behaviorsMap != null)
{
if (initialStateMarked())
{
HashMap<String, Object> stateMap = new HashMap<>(_behaviorsMap.size(), 1);
boolean nullDelta = true;
for (Map.Entry<String, List<ClientBehavior> > entry : _behaviorsMap.entrySet())
{
// The list is always an instance of _DeltaList so we can cast to
// PartialStateHolder
PartialStateHolder holder = (PartialStateHolder) entry.getValue();
if (holder.initialStateMarked())
{
Object attachedState = holder.saveState(facesContext);
if (attachedState != null)
{
stateMap.put(entry.getKey(), new _AttachedDeltaWrapper(_behaviorsMap.getClass(),
attachedState));
nullDelta = false;
}
}
else
{
stateMap.put(entry.getKey(), saveAttachedState(facesContext, holder));
nullDelta = false;
}
}
if (nullDelta)
{
return null;
}
return stateMap;
}
else
{
//Save it in the traditional way
HashMap<String, Object> stateMap = new HashMap<>(_behaviorsMap.size(), 1);
for (Map.Entry<String, List<ClientBehavior> > entry : _behaviorsMap.entrySet())
{
stateMap.put(entry.getKey(), saveAttachedState(facesContext, entry.getValue()));
}
return stateMap;
}
}
else
{
return null;
}
}
@SuppressWarnings("unchecked")
private void restoreFullSystemEventListenerClassMap(FacesContext facesContext, Object stateObj)
{
if (stateObj != null)
{
Map<Class<? extends SystemEvent>, Object> stateMap = (Map<Class<? extends SystemEvent>, Object>) stateObj;
_systemEventListenerClassMap = new HashMap<>(stateMap.size(), 1f);
for (Map.Entry<Class<? extends SystemEvent>, Object> entry : stateMap.entrySet())
{
_systemEventListenerClassMap.put(entry.getKey(),
(List<SystemEventListener>) restoreAttachedState(facesContext, entry.getValue()));
}
}
else
{
_systemEventListenerClassMap = null;
}
}
@SuppressWarnings("unchecked")
private void restoreDeltaSystemEventListenerClassMap(FacesContext facesContext, Object stateObj)
{
if (stateObj != null)
{
Map<Class<? extends SystemEvent>, Object> stateMap = (Map<Class<? extends SystemEvent>, Object>) stateObj;
if (_systemEventListenerClassMap == null)
{
_systemEventListenerClassMap = new HashMap<>(stateMap.size(), 1f);
}
for (Map.Entry<Class<? extends SystemEvent>, Object> entry : stateMap.entrySet())
{
Object savedObject = entry.getValue();
if (savedObject instanceof _AttachedDeltaWrapper)
{
StateHolder holderList = (StateHolder) _systemEventListenerClassMap.get(entry.getKey());
holderList.restoreState(facesContext,
((_AttachedDeltaWrapper) savedObject).getWrappedStateObject());
}
else
{
_systemEventListenerClassMap.put(entry.getKey(),
(List<SystemEventListener>) restoreAttachedState(facesContext, savedObject));
}
}
}
}
private Object saveSystemEventListenerClassMap(FacesContext facesContext)
{
if (_systemEventListenerClassMap != null)
{
if (initialStateMarked())
{
HashMap<Class<? extends SystemEvent>, Object> stateMap
= new HashMap<>(_systemEventListenerClassMap.size(), 1);
boolean nullDelta = true;
for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener> > entry
: _systemEventListenerClassMap.entrySet())
{
// The list is always an instance of _DeltaList so we can cast to
// PartialStateHolder
PartialStateHolder holder = (PartialStateHolder) entry.getValue();
if (holder.initialStateMarked())
{
Object attachedState = holder.saveState(facesContext);
if (attachedState != null)
{
stateMap.put(entry.getKey(),
new _AttachedDeltaWrapper(_systemEventListenerClassMap.getClass(), attachedState));
nullDelta = false;
}
}
else
{
stateMap.put(entry.getKey(), saveAttachedState(facesContext, holder));
nullDelta = false;
}
}
if (nullDelta)
{
return null;
}
return stateMap;
}
else
{
//Save it in the traditional way
HashMap<Class<? extends SystemEvent>, Object> stateMap =
new HashMap<>(_systemEventListenerClassMap.size(), 1);
for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener>> entry
: _systemEventListenerClassMap.entrySet())
{
stateMap.put(entry.getKey(), saveAttachedState(facesContext, entry.getValue()));
}
return stateMap;
}
}
else
{
return null;
}
}
/**
* @param string
* the component id, that should be a vaild one.
*/
private void isIdValid(String string)
{
// is there any component identifier ?
if (string == null)
{
return;
}
// Component identifiers must obey the following syntax restrictions:
// 1. Must not be a zero-length String.
if (string.length() == 0)
{
throw new IllegalArgumentException("component identifier must not be a zero-length String");
}
// If new id is the same as old it must be valid
if (string.equals(_id))
{
return;
}
// 2. First character must be a letter or an underscore ('_').
char firstChar = string.charAt(0);
if (!Character.isLetter(firstChar) && firstChar != '_')
{
throw new IllegalArgumentException("component identifier's first character must be a letter "
+ "or an underscore ('_')! But it is \""
+ firstChar + '"');
}
for (int i = 1; i < string.length(); i++)
{
char c = string.charAt(i);
// 3. Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
if (!Character.isLetterOrDigit(c) && c != '-' && c != '_')
{
throw new IllegalArgumentException("Subsequent characters of component identifier must be a letter, "
+ "a digit, an underscore ('_'), or a dash ('-')! "
+ "But component identifier \"" + string + "\" contains \""
+ c + '"');
}
}
}
private boolean _isPhaseExecutable(FacesContext context)
{
if (context == null)
{
throw new NullPointerException("context");
}
// If the rendered property of this UIComponent is false, skip further processing.
return isRendered();
}
@Override
boolean isCachedFacesContext()
{
return _facesContext != null;
}
@Override
void setCachedFacesContext(FacesContext facesContext)
{
_facesContext = facesContext;
}
Renderer getCachedRenderer()
{
return _cachedRenderer;
}
void setCachedRenderer(Renderer renderer)
{
_cachedRenderer = renderer;
}
Boolean isCachedIsRendered()
{
return _cachedIsRendered;
}
void setCachedIsRendered(Boolean rendered)
{
_cachedIsRendered = rendered;
}
void setOamVfMarkCreated(String markCreated)
{
_markCreated = markCreated;
}
String getOamVfMarkCreated()
{
return _markCreated;
}
String getOamVfFacetName()
{
return _facetName;
}
void setOamVfFacetName(String facetName)
{
_facetName = facetName;
}
boolean isOamVfAddedByHandler()
{
return (_capabilities & FLAG_ADDED_BY_HANDLER) != 0;
}
void setOamVfAddedByHandler(boolean addedByHandler)
{
if (addedByHandler)
{
_capabilities |= FLAG_ADDED_BY_HANDLER;
}
else
{
_capabilities &= ~(FLAG_ADDED_BY_HANDLER);
}
}
boolean isOamVfFacetCreatedUIPanel()
{
return (_capabilities & FLAG_FACET_CREATED_UIPANEL) != 0;
}
void setOamVfFacetCreatedUIPanel(boolean facetCreatedUIPanel)
{
if (facetCreatedUIPanel)
{
_capabilities |= FLAG_FACET_CREATED_UIPANEL;
}
else
{
_capabilities &= ~(FLAG_FACET_CREATED_UIPANEL);
}
}
/**
* <p>
* This gets a single FacesContext-local shared stringbuilder instance, each time you call
* _getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
* </p><p>
* This allows you to use the same StringBuilder instance over and over.
* You must call toString on the instance before calling _getSharedStringBuilder again.
* </p>
* Example that works
* <pre><code>
* StringBuilder sb1 = _getSharedStringBuilder();
* sb1.append(a).append(b);
* String c = sb1.toString();
*
* StringBuilder sb2 = _getSharedStringBuilder();
* sb2.append(b).append(a);
* String d = sb2.toString();
* </code></pre>
* <br><br>
* Example that doesn't work, you must call toString on sb1 before
* calling _getSharedStringBuilder again.
* <pre><code>
* StringBuilder sb1 = _getSharedStringBuilder();
* StringBuilder sb2 = _getSharedStringBuilder();
*
* sb1.append(a).append(b);
* String c = sb1.toString();
*
* sb2.append(b).append(a);
* String d = sb2.toString();
* </code></pre>
*
* @param facesContext
*/
static StringBuilder _getSharedStringBuilder(FacesContext facesContext)
{
Map<Object, Object> attributes = facesContext.getAttributes();
StringBuilder sb = (StringBuilder) attributes.get(_STRING_BUILDER_KEY);
if (sb == null)
{
sb = new StringBuilder();
attributes.put(_STRING_BUILDER_KEY, sb);
}
else
{
// clear out the stringBuilder by setting the length to 0
sb.setLength(0);
}
return sb;
}
// ------------------ GENERATED CODE BEGIN (do not modify!) --------------------
private static final Boolean DEFAULT_RENDERED = Boolean.TRUE;
@Override
public void setRendered(boolean rendered)
{
getStateHelper().put(PropertyKeys.rendered, rendered );
setCachedIsRendered(null);
}
@Override
public void setRendererType(String rendererType)
{
this._rendererType = rendererType;
if (initialStateMarked())
{
//This flag just indicates the rendererType
//should be included on the delta
_capabilities |= FLAG_IS_RENDERER_TYPE_SET;
}
setCachedRenderer(null);
}
// ------------------ GENERATED CODE END ---------------------------------------
private Map<String, List<ClientBehavior>> wrapBehaviorsMap()
{
if (_unmodifiableBehaviorsMap == null)
{
_unmodifiableBehaviorsMap = Collections.unmodifiableMap(_behaviorsMap);
}
return _unmodifiableBehaviorsMap;
}
}