blob: 110c14b05d62310feec26c56e040dafe9f24230b [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 java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.FactoryFinder;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.Renderer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 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.
* <p>
* See Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a> for more.
*
* @JSFComponent
* type = "javax.faces.ComponentBase"
* family = "javax.faces.ComponentBase"
* desc = "base component from which all components should inherit"
* tagClass = "javax.faces.webapp.UIComponentTag"
*
* @JSFJspProperty
* name = "binding"
* returnType = "java.lang.String"
* 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"
*
* @author Manfred Geiler (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public abstract class UIComponentBase
extends UIComponent
{
private static Log log = LogFactory.getLog(UIComponentBase.class);
private static final Iterator _EMPTY_UICOMPONENT_ITERATOR =
new _EmptyIterator();
private _ComponentAttributesMap _attributesMap = null;
private Map _valueBindingMap = null;
private List _childrenList = null;
private Map _facetMap = null;
private List _facesListeners = null;
private String _clientId = null;
private String _id = null;
private UIComponent _parent = null;
private boolean _transient = false;
private transient FacesContext _facesContext;
public UIComponentBase()
{
}
/**
* 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.
*/
public Map getAttributes()
{
if (_attributesMap == null)
{
_attributesMap = new _ComponentAttributesMap(this);
}
return _attributesMap;
}
/**
* Get the named value-binding associated with this component.
* <p>
* Value-bindings are stored in a map associated with the component,
* though there is commonly a property (setter/getter methods)
* of the same name defined on the component itself which
* evaluates the value-binding when called.
*/
public ValueBinding getValueBinding(String name)
{
if (name == null) throw new NullPointerException("name");
if (_valueBindingMap == null)
{
return null;
}
else
{
return (ValueBinding)_valueBindingMap.get(name);
}
}
/**
* Put the provided value-binding into a map of value-bindings
* associated with this component.
*/
public void setValueBinding(String name,
ValueBinding binding)
{
if (name == null) throw new NullPointerException("name");
if (_valueBindingMap == null)
{
_valueBindingMap = new HashMap();
}
_valueBindingMap.put(name, binding);
}
/**
* 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.
*/
public String getClientId(FacesContext context)
{
if (context == null) throw new NullPointerException("context");
if (_clientId != null) return _clientId;
boolean idWasNull = false;
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
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot != null)
{
id = viewRoot.createUniqueId();
}
else
{
context.getExternalContext().log("ERROR: Cannot automatically create an id for component of type " + getClass().getName() + " because there is no viewRoot in the current facesContext!");
id = "ERROR";
}
setId(id);
//We remember that the id was null and log a warning down below
idWasNull = true;
}
UIComponent namingContainer = findParentNamingContainer(this, false);
if (namingContainer != null)
{
_clientId = namingContainer.getClientId(context) + NamingContainer.SEPARATOR_CHAR + id;
}
else
{
_clientId = id;
}
Renderer renderer = getRenderer(context);
if (renderer != null)
{
_clientId = renderer.convertClientId(context, _clientId);
}
if (idWasNull)
{
context.getExternalContext().log("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!");
}
return _clientId;
}
/**
* An identifier for this particular component instance within
* a component view.
* <p>
* The id must be unique within the scope of the tag's enclosing
* NamingContainer (eg h:form or f:subview). The id is
* not necessarily unique across all components in the current view
* </p>
* <p>
* This value must be a static value, ie not change over the lifetime
* of a component. It cannot be defined via an EL expression; only
* a string is permitted.
* </p>
*
* @JSFProperty
*/
public String getId()
{
return _id;
}
/**
* 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.
*/
public void setId(String id)
{
isIdValid(id);
_id = id;
_clientId = null;
UIComponent parent = getParent();
if(parent != null) {
List li = getParent().getChildren();
if(li instanceof _ComponentChildrenList) {
((_ComponentChildrenList) li).updateId(_id,this);
}
}
}
public UIComponent getParent()
{
return _parent;
}
public void setParent(UIComponent parent)
{
_parent = parent;
}
/**
* 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>
*/
public boolean getRendersChildren()
{
Renderer renderer = getRenderer(getFacesContext());
if (renderer != null)
{
return renderer.getRendersChildren();
}
else
{
return false;
}
}
/**
* 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>
*/
public List getChildren()
{
if (_childrenList == null)
{
_childrenList = new _ComponentChildrenList(this);
}
return _childrenList;
}
/**
* 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.
*/
public int getChildCount()
{
return _childrenList == null ? 0 : _childrenList.size();
}
/**
* 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.
*/
public UIComponent findComponent(String expr)
{
if (expr == null) throw new NullPointerException("expr");
if (expr.length() == 0) throw new IllegalArgumentException("empty expr"); //TODO: not specified!
UIComponent findBase;
if (expr.charAt(0) == NamingContainer.SEPARATOR_CHAR)
{
findBase = getRootComponent(this);
expr = expr.substring(1);
}
else
{
if (this instanceof NamingContainer)
{
findBase = this;
}
else
{
findBase = findParentNamingContainer(this, true /* root if not found */);
}
}
int separator = expr.indexOf(NamingContainer.SEPARATOR_CHAR);
if (separator == -1)
{
return findComponent(findBase, expr);
}
else
{
String id = expr.substring(0, separator);
findBase = findComponent(findBase, id);
if (findBase == null)
{
return null;
}
else
{
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));
}
}
}
static UIComponent findParentNamingContainer(UIComponent component,
boolean returnRootIfNotFound)
{
UIComponent parent = component.getParent();
if (returnRootIfNotFound && parent == null)
{
return component;
}
while (parent != null)
{
if (parent instanceof NamingContainer) return parent;
if (returnRootIfNotFound)
{
UIComponent nextParent = parent.getParent();
if (nextParent == null)
{
return parent; //Root
}
parent = nextParent;
}
else
{
parent = parent.getParent();
}
}
return null;
}
static UIComponent getRootComponent(UIComponent component)
{
UIComponent parent;
for(;;)
{
parent = component.getParent();
if (parent == null) return component;
component = parent;
}
}
/**
* Find the component with the specified id starting from the specified
* component.
* <p>
* Param id must not contain any NamingContainer.SEPARATOR_CHAR characters
* (ie ":"). This method explicitly does <i>not</i> search into any
* child naming container components; this is expected to be handled
* by the caller of this method.
* <p>
* For an implementation of findComponent which does descend into
* child naming components, see org.apache.myfaces.custom.util.ComponentUtils.
*
* @return findBase, a descendant of findBase, or null.
*/
private UIComponent findComponent(UIComponent findBase, String id)
{
if (idsAreEqual(id, findBase)){
return findBase;
}
/*UIComponent comp = findComponentCached(id, findBase);
if(comp != null)
return comp;*/
return findComponentNormal(id, findBase);
}
private UIComponent findComponentCached(String id, UIComponent findBase) {
if(!(findBase.getChildren() instanceof _ComponentChildrenList)) {
return null;
}
_ComponentChildrenList li = (_ComponentChildrenList) findBase.getChildren();
UIComponent comp = li.get(id);
if(comp != null)
return comp;
for (Iterator it = findBase.getFacetsAndChildren(); it.hasNext(); )
{
UIComponent childOrFacet = (UIComponent)it.next();
UIComponent find = findComponentCached(id,childOrFacet);
if (find != null) return find;
}
return null;
}
private UIComponent findComponentNormal(String id, UIComponent findBase) {
if (idsAreEqual(id, findBase))
{
return findBase;
}
for (Iterator it = findBase.getFacetsAndChildren(); it.hasNext(); )
{
UIComponent childOrFacet = (UIComponent)it.next();
// If a descendant NamingContainer is found,
// child components including facets are not searched,
// so just check if this component is and continue
// if not.
if (childOrFacet instanceof NamingContainer){
if (idsAreEqual(id, childOrFacet))
{
return childOrFacet;
}
}else{
UIComponent find = findComponentNormal(id, childOrFacet);
if (find != null) return find;
}
}
return null;
}
/*
* Return true if the specified component matches the provided id.
* This needs some quirks to handle components whose id value gets
* dynamically "tweaked", eg a UIData component whose id gets
* the current row index appended to it.
*/
private boolean idsAreEqual(String id, UIComponent cmp)
{
if(id.equals(cmp.getId()))
return true;
if(cmp instanceof UIData)
{
UIData uiData = ((UIData) cmp);
if(uiData.getRowIndex()==-1)
{
return dynamicIdIsEqual(id,cmp.getId());
}
else
{
return id.equals(cmp.getId()+NamingContainer.SEPARATOR_CHAR+uiData.getRowIndex());
}
}
return false;
}
private boolean dynamicIdIsEqual(String dynamicId, String id)
{
return dynamicId.matches(id+":[0-9]*");
}
public Map getFacets()
{
if (_facetMap == null)
{
_facetMap = new _ComponentFacetMap(this);
}
return _facetMap;
}
public UIComponent getFacet(String name)
{
return _facetMap == null ? null : (UIComponent)_facetMap.get(name);
}
public Iterator getFacetsAndChildren()
{
if (_facetMap == null)
{
if (_childrenList == null)
return _EMPTY_UICOMPONENT_ITERATOR;
if (_childrenList.size() == 0)
return _EMPTY_UICOMPONENT_ITERATOR;
return _childrenList.iterator();
}
else
{
if (_facetMap.size() == 0)
{
if (_childrenList == null)
return _EMPTY_UICOMPONENT_ITERATOR;
if (_childrenList.size() == 0)
return _EMPTY_UICOMPONENT_ITERATOR;
return _childrenList.iterator();
}
else
{
if (_childrenList == null)
return _facetMap.values().iterator();
if (_childrenList.size() == 0)
return _facetMap.values().iterator();
return new _FacetsAndChildrenIterator(_facetMap, _childrenList);
}
}
}
/**
* 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.
*/
public void broadcast(FacesEvent event)
throws AbortProcessingException
{
if (event == null) throw new NullPointerException("event");
try {
if (_facesListeners == null) return;
for (Iterator it = _facesListeners.iterator(); it.hasNext(); )
{
FacesListener facesListener = (FacesListener)it.next();
if (event.isAppropriateListener(facesListener))
{
event.processListener(facesListener);
}
}
} catch (Exception e) {
if (e instanceof AbortProcessingException) {
throw (AbortProcessingException) e;
}
throw new FacesException("Exception while calling broadcast on : "+getPathToComponent(this), e);
}
}
/**
* 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.
*/
public void decode(FacesContext context)
{
if (context == null) throw new NullPointerException("context");
try {
Renderer renderer = getRenderer(context);
if (renderer != null)
{
renderer.decode(context, this);
}
} catch (Exception e) {
if (e instanceof AbortProcessingException)
throw new FacesException("Exception while calling decode on : "+getPathToComponent(this), e);
}
}
public void encodeBegin(FacesContext context)
throws IOException
{
if (context == null) throw new NullPointerException("context");
try {
setCachedFacesContext(context);
if (!isRendered()) return;
Renderer renderer = getRenderer(context);
if (renderer != null)
{
renderer.encodeBegin(context, this);
}
} catch (Exception e) {
throw new FacesException("Exception while calling encodeBegin on : "+getPathToComponent(this), e);
}
finally
{
setCachedFacesContext(null);
}
}
public void encodeChildren(FacesContext context)
throws IOException
{
if (context == null) throw new NullPointerException("context");
boolean isCachedFacesContext = isCachedFacesContext();
try
{
if (!isCachedFacesContext)
{
setCachedFacesContext(context);
}
if (!isRendered()) return;
Renderer renderer = getRenderer(context);
if (renderer != null)
{
renderer.encodeChildren(context, this);
}
}
finally
{
if (!isCachedFacesContext)
{
setCachedFacesContext(null);
}
}
}
public void encodeEnd(FacesContext context)
throws IOException
{
if (context == null) throw new NullPointerException("context");
try {
setCachedFacesContext(context);
if (!isRendered()) return;
Renderer renderer = getRenderer(context);
if (renderer != null)
{
renderer.encodeEnd(context, this);
}
} catch (Exception e) {
throw new FacesException("Exception while calling encodeEnd on : "+getPathToComponent(this), e);
}
finally
{
setCachedFacesContext(null);
}
}
protected void addFacesListener(FacesListener listener)
{
if (listener == null) throw new NullPointerException("listener");
if (_facesListeners == null)
{
_facesListeners = new ArrayList();
}
_facesListeners.add(listener);
}
protected FacesListener[] getFacesListeners(Class clazz)
{
if (_facesListeners == null)
{
return (FacesListener[])Array.newInstance(clazz, 0);
}
List lst = null;
for (Iterator it = _facesListeners.iterator(); it.hasNext(); )
{
FacesListener facesListener = (FacesListener)it.next();
if (clazz.isAssignableFrom(facesListener.getClass()))
{
if (lst == null) lst = new ArrayList();
lst.add(facesListener);
}
}
if (lst == null)
{
return (FacesListener[])Array.newInstance(clazz, 0);
}
else
{
return (FacesListener[])lst.toArray((FacesListener[])Array.newInstance(clazz, lst.size()));
}
}
protected void removeFacesListener(FacesListener listener)
{
if (_facesListeners != null)
{
_facesListeners.remove(listener);
}
}
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);
}
public void processDecodes(FacesContext context)
{
if (context == null)
throw new NullPointerException("context");
try
{
setCachedFacesContext(context);
if (!isRendered()) return;
for (Iterator it = getFacetsAndChildren(); it.hasNext(); )
{
UIComponent childOrFacet = (UIComponent)it.next();
childOrFacet.processDecodes(context);
}
try
{
decode(context);
}
catch (RuntimeException e)
{
context.renderResponse();
throw e;
}
}
finally
{
setCachedFacesContext(null);
}
}
public void processValidators(FacesContext context)
{
if (context == null) throw new NullPointerException("context");
try
{
setCachedFacesContext(context);
if (!isRendered()) return;
for (Iterator it = getFacetsAndChildren(); it.hasNext(); )
{
UIComponent childOrFacet = (UIComponent)it.next();
childOrFacet.processValidators(context);
}
}
finally
{
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.
*/
public void processUpdates(FacesContext context)
{
if (context == null) throw new NullPointerException("context");
try
{
setCachedFacesContext(context);
if (!isRendered()) return;
for (Iterator it = getFacetsAndChildren(); it.hasNext(); )
{
UIComponent childOrFacet = (UIComponent)it.next();
childOrFacet.processUpdates(context);
}
}
finally
{
setCachedFacesContext(null);
}
}
public Object processSaveState(FacesContext context)
{
if (context == null) throw new NullPointerException("context");
if (isTransient()) return null;
Map facetMap = null;
for (Iterator it = getFacets().entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry)it.next();
if (facetMap == null) facetMap = new HashMap();
UIComponent component = (UIComponent)entry.getValue();
if (!component.isTransient())
{
facetMap.put(entry.getKey(), component.processSaveState(context));
}
}
List childrenList = null;
if (getChildCount() > 0)
{
for (Iterator it = getChildren().iterator(); it.hasNext(); )
{
UIComponent child = (UIComponent)it.next();
if (childrenList == null) {
childrenList = new ArrayList(getChildCount());
}
Object childState = child.processSaveState(context);
if (childState != null) {
childrenList.add(childState);
}
}
}
Object savedState;
try {
savedState = saveState(context);
} catch (Exception e) {
throw new FacesException("Exception while saving state of component : "+getPathToComponent(this), e);
}
return new Object[] {savedState,
facetMap,
childrenList};
}
public void processRestoreState(FacesContext context,
Object state)
{
if (context == null) throw new NullPointerException("context");
Object myState = ((Object[])state)[0];
Map facetMap = (Map)((Object[])state)[1];
List childrenList = (List)((Object[])state)[2];
if(facetMap != null)
{
for (Iterator it = getFacets().entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry)it.next();
Object facetState = facetMap.get(entry.getKey());
if (facetState != null)
{
UIComponent component = (UIComponent)entry.getValue();
component.processRestoreState(context, facetState);
}
else
{
context.getExternalContext().log("No state found to restore facet " + entry.getKey());
}
}
}
if (childrenList != null && getChildCount() > 0)
{
int idx = 0;
for (Iterator it = getChildren().iterator(); it.hasNext(); )
{
UIComponent child = (UIComponent)it.next();
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());
}
}
}
}
try {
restoreState(context, myState);
} catch (Exception e) {
throw new FacesException("Exception while restoring state of component : "+getPathToComponent(this), e);
}
}
protected FacesContext getFacesContext()
{
if (_facesContext == null)
{
return FacesContext.getCurrentInstance();
}
else
{
return _facesContext;
}
}
protected Renderer getRenderer(FacesContext context)
{
if (context == null) throw new NullPointerException("context");
String rendererType = getRendererType();
if (rendererType == null) return null;
RenderKit renderKit = context.getRenderKit();
Renderer renderer = renderKit.getRenderer(getFamily(), rendererType);
if (renderer == null)
{
getFacesContext().getExternalContext().log("No Renderer found for component " + getPathToComponent(this) + " (component-family=" + getFamily() + ", renderer-type=" + rendererType + ")");
log.warn("No Renderer found for component " + getPathToComponent(this) + " (component-family=" + getFamily() + ", renderer-type=" + rendererType + ")");
}
return renderer;
}
private String getPathToComponent(UIComponent component)
{
StringBuffer buf = new StringBuffer();
if(component == null)
{
buf.append("{Component-Path : ");
buf.append("[null]}");
return buf.toString();
}
getPathToComponent(component,buf);
buf.insert(0,"{Component-Path : ");
buf.append("}");
return buf.toString();
}
private static void getPathToComponent(UIComponent component, StringBuffer buf)
{
if(component == null)
return;
StringBuffer intBuf = new StringBuffer();
intBuf.append("[Class: ");
intBuf.append(component.getClass().getName());
if(component instanceof UIViewRoot)
{
intBuf.append(",ViewId: ");
intBuf.append(((UIViewRoot) component).getViewId());
}
else
{
intBuf.append(",Id: ");
intBuf.append(component.getId());
}
intBuf.append("]");
buf.insert(0,intBuf.toString());
getPathToComponent(component.getParent(), buf);
}
/**
* @JSFProperty
* literalOnly = "true"
* transient = "true"
* tagExcluded ="true"
*/
public boolean isTransient()
{
return _transient;
}
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 (attachedObject == null) return null;
if (attachedObject instanceof List)
{
List lst = new ArrayList(((List)attachedObject).size());
for (Iterator it = ((List)attachedObject).iterator(); it.hasNext(); )
{
Object value = it.next();
if (value != null)
{
lst.add(saveAttachedState(context, value));
}
}
return new _AttachedListStateWrapper(lst);
}
else if (attachedObject instanceof StateHolder)
{
if (((StateHolder)attachedObject).isTransient())
{
return null;
}
else
{
return new _AttachedStateWrapper(attachedObject.getClass(),
((StateHolder)attachedObject).saveState(context));
}
}
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)
{
List lst = ((_AttachedListStateWrapper)stateObj).getWrappedStateList();
List restoredList = new ArrayList(lst.size());
for (Iterator it = lst.iterator(); it.hasNext(); )
{
restoredList.add(restoreAttachedState(context, it.next()));
}
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)
{
Object wrappedState = ((_AttachedStateWrapper)stateObj).getWrappedStateObject();
((StateHolder)restoredObject).restoreState(context, wrappedState);
}
return restoredObject;
}
else
{
return stateObj;
}
}
/**
* 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.
*/
public Object saveState(FacesContext context)
{
Object values[] = new Object[7];
values[0] = _id;
values[1] = _rendered;
values[2] = _rendererType;
values[3] = _clientId;
values[4] = saveAttributesMap();
values[5] = saveAttachedState(context, _facesListeners);
values[6] = saveValueBindingMap(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.
*/
public void restoreState(FacesContext context, Object state)
{
Object values[] = (Object[])state;
_id = (String)values[0];
_rendered = (Boolean)values[1];
_rendererType = (String)values[2];
_clientId = (String)values[3];
restoreAttributesMap(values[4]);
_facesListeners = (List)restoreAttachedState(context, values[5]);
restoreValueBindingMap(context, values[6]);
}
private Object saveAttributesMap()
{
if (_attributesMap != null)
{
return _attributesMap.getUnderlyingMap();
}
else
{
return null;
}
}
private void restoreAttributesMap(Object stateObj)
{
if (stateObj != null)
{
_attributesMap = new _ComponentAttributesMap(this, (Map)stateObj);
}
else
{
_attributesMap = null;
}
}
private Object saveValueBindingMap(FacesContext context)
{
if (_valueBindingMap != null)
{
int initCapacity = (_valueBindingMap.size() * 4 + 3) / 3;
HashMap stateMap = new HashMap(initCapacity);
for (Iterator it = _valueBindingMap.entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry)it.next();
stateMap.put(entry.getKey(),
saveAttachedState(context, entry.getValue()));
}
return stateMap;
}
else
{
return null;
}
}
private void restoreValueBindingMap(FacesContext context, Object stateObj)
{
if (stateObj != null)
{
Map stateMap = (Map)stateObj;
int initCapacity = (stateMap.size() * 4 + 3) / 3;
_valueBindingMap = new HashMap(initCapacity);
for (Iterator it = stateMap.entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry)it.next();
_valueBindingMap.put(entry.getKey(),
restoreAttachedState(context, entry.getValue()));
}
}
else
{
_valueBindingMap = 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");
}
//let's look at all chars inside of the ID if it is a valid ID!
char[] chars = string.toCharArray();
//2. First character must be a letter or an underscore ('_').
if(!Character.isLetter(chars[0]) && chars[0] !='_')
{
throw new IllegalArgumentException("component identifier's first character must be a letter or an underscore ('_')! But it is \""+chars[0]+"\"");
}
for (int i = 1; i < chars.length; i++)
{
//3. Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
if(!Character.isDigit(chars[i]) && !Character.isLetter(chars[i]) && chars[i] !='-' && chars[i] !='_')
{
throw new IllegalArgumentException("Subsequent characters of component identifier must be a letter, a digit, an underscore ('_'), or a dash ('-')! But component identifier contains \""+chars[i]+"\"");
}
}
}
boolean isCachedFacesContext()
{
return _facesContext != null;
}
void setCachedFacesContext(FacesContext facesContext)
{
_facesContext = facesContext;
}
//------------------ GENERATED CODE BEGIN (do not modify!) --------------------
private static final boolean DEFAULT_RENDERED = true;
private Boolean _rendered = null;
private String _rendererType = null;
public void setRendered(boolean rendered)
{
_rendered = Boolean.valueOf(rendered);
}
/**
* A boolean value that indicates whether this component should be rendered.
* Default value: true.
*
* @JSFProperty
*/
public boolean isRendered()
{
if (_rendered != null) return _rendered.booleanValue();
ValueBinding vb = getValueBinding("rendered");
Boolean v = vb != null ? (Boolean)vb.getValue(getFacesContext()) : null;
return v != null ? v.booleanValue() : DEFAULT_RENDERED;
}
public void setRendererType(String rendererType)
{
_rendererType = rendererType;
}
public String getRendererType()
{
if (_rendererType != null) return _rendererType;
ValueBinding vb = getValueBinding("rendererType");
return vb != null ? _ComponentUtils.getStringValue(getFacesContext(), vb) : null;
}
//------------------ GENERATED CODE END ---------------------------------------
}