| /* |
| * 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.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| 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.FactoryFinder; |
| 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 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 java.util.Map.Entry; |
| |
| /** |
| * 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. |
| * |
| * @author Manfred Geiler (latest modification by $Author$) |
| * @version $Revision$ $Date$ |
| */ |
| @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 Log log = LogFactory.getLog(UIComponentBase.class); |
| |
| private static final ThreadLocal<StringBuilder> _STRING_BUILDER = |
| new ThreadLocal<StringBuilder>(); |
| |
| private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR = |
| new _EmptyIterator<UIComponent>(); |
| |
| private _ComponentAttributesMap _attributesMap = null; |
| private List<UIComponent> _childrenList = null; |
| private Map<String,UIComponent> _facetMap = null; |
| private List<FacesListener> _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<String, Object> 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. |
| * |
| * @deprecated Replaced by getValueExpression |
| */ |
| public ValueBinding getValueBinding(String name) |
| { |
| ValueExpression expression = getValueExpression(name); |
| if (expression != null) |
| { |
| if (expression instanceof _ValueBindingToValueExpression) |
| { |
| return ((_ValueBindingToValueExpression) expression).getValueBinding(); |
| } |
| return new _ValueExpressionToValueBinding(expression); |
| } |
| return null; |
| } |
| |
| /** |
| * Put the provided value-binding into a map of value-bindings |
| * associated with this component. |
| * |
| * @deprecated Replaced by setValueExpression |
| */ |
| public void setValueBinding(String name, |
| ValueBinding binding) |
| { |
| setValueExpression(name, binding == null ? null : new _ValueBindingToValueExpression(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 |
| { |
| // The RI throws a NPE |
| throw new FacesException( |
| "Cannot create clientId. No id is assigned for component to create an id and UIViewRoot is not defined: " |
| + getPathToComponent(this)); |
| } |
| setId(id); |
| //We remember that the id was null and log a warning down below |
| idWasNull = true; |
| } |
| |
| UIComponent namingContainer = _ComponentUtils.findParentNamingContainer(this, false); |
| if (namingContainer != null) |
| { |
| String containerClientId = namingContainer.getContainerClientId(context); |
| if (containerClientId != null ) |
| { |
| StringBuilder bld = __getSharedStringBuilder(); |
| _clientId = bld.append(containerClientId).append(NamingContainer.SEPARATOR_CHAR).append(id).toString(); |
| } |
| else |
| { |
| _clientId = id; |
| } |
| } |
| else |
| { |
| _clientId = id; |
| } |
| |
| Renderer renderer = getRenderer(context); |
| if (renderer != null) |
| { |
| _clientId = renderer.convertClientId(context, _clientId); |
| } |
| |
| if (idWasNull && log.isWarnEnabled()) |
| { |
| log.warn("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: " + getPathToComponent(this)); |
| } |
| |
| return _clientId; |
| } |
| |
| /** |
| * 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) |
| public String getId() |
| { |
| return _id; |
| } |
| |
| |
| /** |
| * <code>invokeOnComponent</code> must be implemented in <code>UIComponentBase</code> too... |
| */ |
| 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); |
| } |
| } |
| } |
| |
| /** |
| * 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; |
| } |
| |
| 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()); |
| return renderer != null ? renderer.getRendersChildren() : 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<UIComponent> 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) |
| return null; |
| |
| UIComponent findBase; |
| if (expr.charAt(0) == NamingContainer.SEPARATOR_CHAR) |
| { |
| 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(NamingContainer.SEPARATOR_CHAR); |
| if (separator == -1) |
| { |
| return _ComponentUtils.findComponent(findBase, expr); |
| } |
| |
| String id = expr.substring(0, separator); |
| findBase = _ComponentUtils.findComponent(findBase, id); |
| 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)); |
| |
| } |
| |
| |
| public Map<String, UIComponent> getFacets() |
| { |
| if (_facetMap == null) |
| { |
| _facetMap = new _ComponentFacetMap<UIComponent>(this); |
| } |
| return _facetMap; |
| } |
| |
| public UIComponent getFacet(String name) |
| { |
| return _facetMap == null ? null : _facetMap.get(name); |
| } |
| |
| 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 _EMPTY_UICOMPONENT_ITERATOR; |
| |
| return getChildren().iterator(); |
| } |
| else |
| { |
| if (getChildCount() == 0) |
| return getFacets().values().iterator(); |
| |
| return new _FacetsAndChildrenIterator(getFacets(), getChildren()); |
| } |
| } |
| |
| /** |
| * 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<FacesListener> it = _facesListeners.iterator(); it.hasNext(); ) |
| { |
| FacesListener facesListener = it.next(); |
| if (event.isAppropriateListener(facesListener)) |
| { |
| event.processListener(facesListener); |
| } |
| } |
| } |
| catch(Exception ex) { |
| if (ex instanceof AbortProcessingException) { |
| throw (AbortProcessingException) ex; |
| } |
| throw new FacesException("Exception while calling broadcast on component : "+getPathToComponent(this), ex); |
| } |
| } |
| |
| /** |
| * 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 ex) { |
| throw new FacesException("Exception while decoding component : "+getPathToComponent(this), ex); |
| } |
| } |
| |
| 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 ex) { |
| throw new FacesException("Exception while calling encodeBegin on component : "+getPathToComponent(this), ex); |
| } |
| 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 ex) { |
| throw new FacesException("Exception while calling encodeEnd on component : "+getPathToComponent(this), ex); |
| } |
| finally |
| { |
| setCachedFacesContext(null); |
| } |
| } |
| |
| protected void addFacesListener(FacesListener listener) |
| { |
| if (listener == null) throw new NullPointerException("listener"); |
| if (_facesListeners == null) |
| { |
| _facesListeners = new ArrayList<FacesListener>(); |
| } |
| _facesListeners.add(listener); |
| } |
| |
| 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; |
| for (Iterator<FacesListener> it = _facesListeners.iterator(); it.hasNext();) |
| { |
| FacesListener facesListener = it.next(); |
| if (clazz.isAssignableFrom(facesListener.getClass())) |
| { |
| if (lst == null) |
| lst = new ArrayList<FacesListener>(); |
| lst.add(facesListener); |
| } |
| } |
| if (lst == null) |
| { |
| return (FacesListener[]) Array.newInstance(clazz, 0); |
| } |
| |
| return lst.toArray((FacesListener[])Array.newInstance(clazz, lst.size())); |
| } |
| |
| protected void removeFacesListener(FacesListener listener) |
| { |
| if(listener == null) |
| { |
| throw new NullPointerException("listener is null"); |
| } |
| |
| 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<UIComponent> it = getFacetsAndChildren(); it.hasNext();) |
| { |
| it.next().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<UIComponent> it = getFacetsAndChildren(); it.hasNext(); ) |
| { |
| it.next().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<UIComponent> it = getFacetsAndChildren(); it.hasNext(); ) |
| { |
| it.next().processUpdates(context); |
| } |
| } |
| finally |
| { |
| setCachedFacesContext(null); |
| } |
| } |
| |
| public Object processSaveState(FacesContext context) |
| { |
| if (context == null) |
| throw new NullPointerException("context"); |
| if (isTransient()) |
| return null; |
| Map<String, Object> facetMap = null; |
| int facetCount = getFacetCount(); |
| if (facetCount > 0) |
| { |
| for (Iterator<Entry<String, UIComponent>> it = getFacets().entrySet().iterator(); it.hasNext();) |
| { |
| Entry<String, UIComponent> entry = it.next(); |
| UIComponent component = entry.getValue(); |
| if (!component.isTransient()) |
| { |
| if (facetMap == null) |
| facetMap = new HashMap<String, Object>(facetCount, 1); |
| facetMap.put(entry.getKey(), component.processSaveState(context)); |
| } |
| } |
| } |
| List<Object> childrenList = null; |
| int childCount = getChildCount(); |
| if (childCount > 0) |
| { |
| for (Iterator it = getChildren().iterator(); it.hasNext();) |
| { |
| UIComponent child = (UIComponent) it.next(); |
| if (!child.isTransient()) |
| { |
| if (childrenList == null) |
| { |
| childrenList = new ArrayList<Object>(childCount); |
| } |
| Object childState = child.processSaveState(context); |
| if (childState != null) |
| { |
| childrenList.add(childState); |
| } |
| } |
| } |
| } |
| |
| Object savedState; |
| try { |
| savedState = saveState(context); |
| } catch(Exception ex) { |
| throw new FacesException("Exception while saving state of component : "+getPathToComponent(this), ex); |
| } |
| |
| return new Object[] { savedState, facetMap, childrenList }; |
| } |
| |
| public void processRestoreState(FacesContext context, |
| Object state) |
| { |
| if (context == null) throw new NullPointerException("context"); |
| Object[] stateValues = (Object[]) state; |
| Object myState = stateValues[0]; |
| Map<String, Object> facetMap = (Map<String, Object>)stateValues[1]; |
| List<Object> childrenList = (List<Object>)stateValues[2]; |
| if(facetMap != null && getFacetCount() > 0) |
| { |
| for (Iterator<Entry<String, UIComponent>> it = getFacets().entrySet().iterator(); it.hasNext(); ) |
| { |
| Entry<String, UIComponent> entry = it.next(); |
| 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()); |
| } |
| } |
| } |
| if (childrenList != null && getChildCount() > 0) |
| { |
| int idx = 0; |
| for (Iterator<UIComponent> it = getChildren().iterator(); it.hasNext(); ) |
| { |
| UIComponent child = 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 ex) { |
| throw new FacesException("Exception while restoring state of component : "+getPathToComponent(this), ex); |
| } |
| } |
| |
| 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 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, |
| istransient = 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<Object> lst = new ArrayList<Object>(((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; |
| } |
| |
| 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<Object> lst = ((_AttachedListStateWrapper)stateObj).getWrappedStateList(); |
| List<Object> restoredList = new ArrayList<Object>(lst.size()); |
| for (Iterator<Object> 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] = saveBindings(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<FacesListener>)restoreAttachedState(context, values[5]); |
| restoreValueExpressionMap(context, values[6]); |
| } |
| |
| |
| private Object saveAttributesMap() |
| { |
| return _attributesMap != null ? _attributesMap.getUnderlyingMap() : null; |
| } |
| |
| private void restoreAttributesMap(Object stateObj) |
| { |
| if (stateObj != null) |
| { |
| _attributesMap = new _ComponentAttributesMap(this, (Map<Object, Object>)stateObj); |
| } |
| else |
| { |
| _attributesMap = null; |
| } |
| } |
| |
| private Object saveBindings(FacesContext context) |
| { |
| if (bindings != null) |
| { |
| HashMap<String, Object> stateMap = new HashMap<String, Object>(bindings.size(), 1); |
| for (Iterator<Entry<String, ValueExpression>> it = bindings.entrySet().iterator(); it.hasNext(); ) |
| { |
| Entry<String, ValueExpression> entry = it.next(); |
| stateMap.put(entry.getKey(), |
| saveAttachedState(context, entry.getValue())); |
| } |
| return stateMap; |
| } |
| |
| return null; |
| } |
| |
| private void restoreValueExpressionMap(FacesContext context, Object stateObj) |
| { |
| if (stateObj != null) |
| { |
| Map stateMap = (Map)stateObj; |
| int initCapacity = (stateMap.size() * 4 + 3) / 3; |
| bindings = new HashMap<String, ValueExpression>(initCapacity); |
| for (Iterator it = stateMap.entrySet().iterator(); it.hasNext(); ) |
| { |
| Map.Entry entry = (Map.Entry)it.next(); |
| bindings.put((String)entry.getKey(), |
| (ValueExpression)restoreAttachedState(context, entry.getValue())); |
| } |
| } |
| else |
| { |
| bindings = 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 ('_'). |
| if(!Character.isLetter(string.charAt(0)) && string.charAt(0) !='_') |
| { |
| throw new IllegalArgumentException("component identifier's first character must be a letter or an underscore ('_')! But it is \""+string.charAt(0)+"\""); |
| } |
| 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 contains \""+c+"\""); |
| } |
| } |
| } |
| |
| <T> T getExpressionValue(String attribute, T explizitValue, T defaultValueIfExpressionNull) |
| { |
| return _ComponentUtils.getExpressionValue(this, attribute, explizitValue, defaultValueIfExpressionNull); |
| } |
| |
| /** |
| * <p> |
| * This gets a single threadlocal 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> |
| * |
| */ |
| static StringBuilder __getSharedStringBuilder() |
| { |
| StringBuilder sb = _STRING_BUILDER.get(); |
| |
| if (sb == null) |
| { |
| sb = new StringBuilder(); |
| _STRING_BUILDER.set(sb); |
| } |
| |
| // clear out the stringBuilder by setting the length to 0 |
| sb.setLength(0); |
| |
| return sb; |
| } |
| |
| 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() |
| { |
| return getExpressionValue("rendered", _rendered, DEFAULT_RENDERED); |
| } |
| |
| public void setRendererType(String rendererType) |
| { |
| _rendererType = rendererType; |
| } |
| |
| public String getRendererType() |
| { |
| return getExpressionValue("rendererType", _rendererType, null); |
| } |
| |
| |
| //------------------ GENERATED CODE END --------------------------------------- |
| |
| /** |
| * @since 1.2 |
| */ |
| |
| public int getFacetCount() |
| { |
| return _facetMap == null ? 0 : _facetMap.size(); |
| } |
| } |