| /* |
| * 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 org.apache.myfaces.trinidad.component; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectOutputStream; |
| |
| import java.net.URL; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import javax.el.ELContext; |
| import javax.el.ELException; |
| import javax.el.MethodExpression; |
| import javax.el.ValueExpression; |
| |
| import javax.faces.FacesException; |
| import javax.faces.application.ProjectStage; |
| import javax.faces.application.Resource; |
| import javax.faces.component.ContextCallback; |
| import javax.faces.component.NamingContainer; |
| import javax.faces.component.StateHolder; |
| import javax.faces.component.UIComponent; |
| import javax.faces.component.UIViewRoot; |
| import javax.faces.component.behavior.Behavior; |
| import javax.faces.component.behavior.ClientBehavior; |
| import javax.faces.component.behavior.ClientBehaviorHolder; |
| import javax.faces.context.ExternalContext; |
| import javax.faces.context.FacesContext; |
| import javax.faces.el.EvaluationException; |
| import javax.faces.el.MethodBinding; |
| import javax.faces.el.ValueBinding; |
| import javax.faces.event.AbortProcessingException; |
| import javax.faces.event.BehaviorEvent; |
| import javax.faces.event.ComponentSystemEvent; |
| import javax.faces.event.ComponentSystemEventListener; |
| import javax.faces.event.FacesEvent; |
| import javax.faces.event.FacesListener; |
| import javax.faces.event.PostAddToViewEvent; |
| import javax.faces.event.PreRemoveFromViewEvent; |
| import javax.faces.event.PreRenderComponentEvent; |
| import javax.faces.event.SystemEvent; |
| import javax.faces.event.SystemEventListener; |
| import javax.faces.render.RenderKit; |
| import javax.faces.render.Renderer; |
| |
| import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent; |
| import org.apache.myfaces.trinidad.bean.AttachedObjects; |
| import org.apache.myfaces.trinidad.bean.FacesBean; |
| import org.apache.myfaces.trinidad.bean.FacesBeanFactory; |
| import org.apache.myfaces.trinidad.bean.PropertyKey; |
| import org.apache.myfaces.trinidad.bean.util.StateUtils; |
| import org.apache.myfaces.trinidad.bean.util.ValueMap; |
| import org.apache.myfaces.trinidad.change.AttributeComponentChange; |
| import org.apache.myfaces.trinidad.change.ComponentChange; |
| import org.apache.myfaces.trinidad.change.ComponentChangeFilter; |
| import org.apache.myfaces.trinidad.change.RowKeySetAttributeChange; |
| import org.apache.myfaces.trinidad.component.ComponentProcessingContext.ProcessingHint; |
| import org.apache.myfaces.trinidad.context.RenderingContext; |
| import org.apache.myfaces.trinidad.context.RequestContext; |
| import org.apache.myfaces.trinidad.event.AttributeChangeEvent; |
| import org.apache.myfaces.trinidad.event.AttributeChangeListener; |
| import org.apache.myfaces.trinidad.logging.TrinidadLogger; |
| import org.apache.myfaces.trinidad.model.RowKeySet; |
| import org.apache.myfaces.trinidad.render.CoreRenderer; |
| import org.apache.myfaces.trinidad.render.ExtendedRenderer; |
| import org.apache.myfaces.trinidad.render.LifecycleRenderer; |
| import org.apache.myfaces.trinidad.util.CollectionUtils; |
| import org.apache.myfaces.trinidad.util.ThreadLocalUtils; |
| |
| |
| /** |
| * Base implementation of components for all of Trinidad. UIXComponentBase |
| * offers a number of features not supplied by the standard UIComponentBase |
| * class: |
| * <ul> |
| * <li>Use of FacesBean for better and easier state saving</li> |
| * <li>Support of the LifecycleRenderer class for greater Renderer |
| * control over the lifecycle</li> |
| * <li>Built-in support for both the "partialTriggers" attribute |
| * (declarative support for being a PPR target) and for triggering |
| * such components (for being a the source of a PPR-causing event).</li> |
| * </ul> |
| * <h3>FacesBean and UIXComponentBase</h3> |
| * <p> |
| * UIXComponentBase differs from UIXComponent most particularly |
| * in its use of FacesBeans to store all state. This offers |
| * a number of advantages: |
| * <ul> |
| * <li>Subclassers - if they use FacesBean for their state as well - |
| * do not need to write overrides of saveState() and restoreState(). |
| * </li> |
| * <li>State is optimized by default</li> |
| * <li>Future optimizations - partly exposed today with |
| * markInitialState() - can offer major state saving improvements. |
| * </ul> |
| * </p> |
| */ |
| // TODO Write Class Javadoc |
| // TODO Thorough review against UIComponentBase |
| @JSFComponent |
| abstract public class UIXComponentBase extends UIXComponent |
| { |
| // Created up top to ensure it's present while we're processing |
| // class initialization code. |
| static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentBase.class); |
| |
| static public final FacesBean.Type TYPE = _createType(); |
| static public final PropertyKey ID_KEY = |
| TYPE.registerKey("id", String.class, PropertyKey.CAP_NOT_BOUND); |
| static public final PropertyKey RENDERED_KEY = |
| TYPE.registerKey("rendered", Boolean.class, Boolean.TRUE); |
| static public final PropertyKey BINDING_KEY = |
| TYPE.registerKey("binding"); |
| static public final PropertyKey TRANSIENT_KEY = |
| TYPE.registerKey("transient", Boolean.class, |
| PropertyKey.CAP_NOT_BOUND | |
| PropertyKey.CAP_TRANSIENT); |
| static public final PropertyKey RENDERER_TYPE_KEY = |
| TYPE.registerKey("rendererType", String.class, PropertyKey.CAP_NOT_BOUND); |
| static private final PropertyKey _LISTENERS_KEY = |
| TYPE.registerKey("listeners", FacesListener[].class, PropertyKey.CAP_LIST); |
| static private final PropertyKey _ATTRIBUTE_CHANGE_LISTENER_KEY = |
| TYPE.registerKey("attributeChangeListener", MethodExpression.class); |
| static private final PropertyKey _CLIENT_BEHAVIORS_KEY = |
| TYPE.registerKey("clientBehaviors", AttachedObjects.class, |
| PropertyKey.CAP_NOT_BOUND|PropertyKey.CAP_PARTIAL_STATE_HOLDER|PropertyKey.CAP_STATE_HOLDER); |
| static private final PropertyKey _SYSTEM_EVENT_LISTENERS_KEY = |
| TYPE.registerKey("systemEventListeners", AttachedObjects.class, |
| PropertyKey.CAP_NOT_BOUND|PropertyKey.CAP_PARTIAL_STATE_HOLDER|PropertyKey.CAP_STATE_HOLDER); |
| static private final PropertyKey _COMPONENT_CHANGE_FILTERS_KEY = |
| TYPE.registerKey("componentChangeFilters", ComponentChangeFilter[].class, PropertyKey.CAP_LIST); |
| static final PropertyKey _PASS_THROUGH_ATTRIBUTES_KEY = |
| TYPE.registerKey("passThroughAttributes", AttachedObjects.class); |
| |
| // =-=AEW "parent", "rendersChildren", "childCount", "children", |
| // "facets", "facetsAndChildren", "family" all are technically |
| // bean properties, but they aren't exposed here... |
| |
| static |
| { |
| // Register a couple of PropertyKeys against names that |
| // the RI's UIComponentTag implementation is shoving |
| // into all components. This is purely an optimization, but |
| // a very useful one. |
| TYPE.registerKey("javax.faces.webapp.COMPONENT_IDS", |
| List.class, |
| PropertyKey.CAP_NOT_BOUND); |
| TYPE.registerKey("javax.faces.webapp.FACET_NAMES", |
| List.class, |
| PropertyKey.CAP_NOT_BOUND); |
| |
| // JSF hammers on this property during component pushing/popping. |
| // Register the PropertyKey to optimize property lookups. |
| TYPE.registerKey(Resource.COMPONENT_RESOURCE_KEY, |
| PropertyKey.CAP_NOT_BOUND); |
| |
| TYPE.lock(); |
| } |
| |
| public UIXComponentBase() |
| { |
| } |
| |
| public UIXComponentBase(String rendererType) |
| { |
| setRendererType(rendererType); |
| } |
| |
| protected FacesBean createFacesBean( |
| String rendererType) |
| { |
| FacesBean bean = FacesBeanFactory.createFacesBean(getClass(), |
| rendererType); |
| UIXFacesBean uixBean = (UIXFacesBean) bean; |
| uixBean.init(this, getBeanType()); |
| return uixBean; |
| } |
| |
| protected PropertyKey getPropertyKey(String name) |
| { |
| PropertyKey key = getBeanType().findKey(name); |
| if (key == null) |
| key = PropertyKey.createPropertyKey(name); |
| |
| return key; |
| } |
| |
| protected FacesBean.Type getBeanType() |
| { |
| return TYPE; |
| } |
| |
| @Override |
| public FacesBean getFacesBean() |
| { |
| if (_facesBean == null) |
| _init(null); |
| |
| return _facesBean; |
| } |
| |
| @Override |
| public String getContainerClientId(FacesContext context, UIComponent child) |
| { |
| return getContainerClientId(context); |
| } |
| |
| @Override |
| public void addAttributeChangeListener(AttributeChangeListener acl) |
| { |
| addFacesListener(acl); |
| } |
| |
| @Override |
| public void removeAttributeChangeListener(AttributeChangeListener acl) |
| { |
| removeFacesListener(acl); |
| } |
| |
| @Override |
| public AttributeChangeListener[] getAttributeChangeListeners() |
| { |
| return (AttributeChangeListener[]) |
| getFacesListeners(AttributeChangeListener.class); |
| } |
| |
| @Override |
| public void setAttributeChangeListener(MethodExpression mb) |
| { |
| setProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY, mb); |
| } |
| |
| @Deprecated |
| public void setAttributeChangeListener(MethodBinding mb) |
| { |
| setAttributeChangeListener(adaptMethodBinding(mb)); |
| } |
| |
| @Override |
| public MethodExpression getAttributeChangeListener() |
| { |
| return (MethodExpression) getProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY); |
| } |
| |
| |
| @Override |
| public ValueExpression getValueExpression(String name) |
| { |
| if (name == null) |
| throw new NullPointerException(); |
| |
| PropertyKey key = getPropertyKey(name); |
| |
| // Support standard RI behavior where getValueBinding() |
| // doesn't complain about being asked for a ValueBinding - |
| // but continue supporting strict behavior at FacesBean layer. |
| if (!key.getSupportsBinding()) |
| return null; |
| |
| return getFacesBean().getValueExpression(key); |
| } |
| |
| @Override |
| public void setValueExpression(String name, |
| ValueExpression expression) |
| { |
| if (name == null) |
| throw new NullPointerException(); |
| |
| if ((expression != null) && expression.isLiteralText()) |
| { |
| ELContext context = |
| FacesContext.getCurrentInstance().getELContext(); |
| getAttributes().put(name, expression.getValue(context)); |
| } |
| else |
| { |
| PropertyKey key = getPropertyKey(name); |
| getFacesBean().setValueExpression(key, expression); |
| } |
| } |
| |
| /** |
| */ |
| @Override |
| public ValueBinding getValueBinding(String name) |
| { |
| if (name == null) |
| throw new NullPointerException(); |
| |
| PropertyKey key = getPropertyKey(name); |
| |
| // Support standard RI behavior where getValueBinding() |
| // doesn't complain about being asked for a ValueBinding - |
| // but continue supporting strict behavior at FacesBean layer. |
| if (!key.getSupportsBinding()) |
| return null; |
| |
| return getFacesBean().getValueBinding(key); |
| } |
| |
| @Override |
| public void setValueBinding(String name, ValueBinding binding) |
| { |
| if (name == null) |
| throw new NullPointerException(); |
| |
| PropertyKey key = getPropertyKey(name); |
| getFacesBean().setValueBinding(key, binding); |
| } |
| |
| @Override |
| public Map<String, Object> getAttributes() |
| { |
| if (_attributes == null) |
| _init(null); |
| |
| return _attributes; |
| } |
| |
| /** |
| * Adds a change for a Component, or the Component's subtree, returning the change actually added, |
| * or <code>null</code>, if no change was added. The proposed change may be rejected by the |
| * component itself, one of its ancestors, or the ChangeManager implementation. |
| * @param change The change to add for this component |
| * @return The ComponentChange actually added, or |
| * <code>null</code> if no change was added. |
| * @see #addComponentChange(UIComponent, ComponentChange) |
| */ |
| public final ComponentChange addComponentChange(ComponentChange change) |
| { |
| return addComponentChange(this, change); |
| } |
| |
| /** |
| * Add a component change filter to this component. |
| * When <code>addComponentChange(ComponentChange)</code> method on this component is called, the ComponentChange will |
| * be added only if it is accepted by all the component change filters attached to this component as well as those |
| * attached to all its ancestors. |
| * @param componentChangeFilter The ComponentChangeFilter instance to add to this component |
| * @see #addComponentChange(ComponentChange) |
| */ |
| public final void addComponentChangeFilter(ComponentChangeFilter componentChangeFilter) |
| { |
| if (componentChangeFilter == null) |
| throw new NullPointerException(); |
| |
| getFacesBean().addEntry(_COMPONENT_CHANGE_FILTERS_KEY, componentChangeFilter); |
| } |
| |
| /** |
| * Remove a component change filter to this component. |
| * @param componentChangeFilter The ComponentChangeFilter instance to remove from this component |
| * @see #addComponentChangeFilter(ComponentChangeFilter) |
| */ |
| public final void removeComponentChangeFilter(ComponentChangeFilter componentChangeFilter) |
| { |
| if (componentChangeFilter == null) |
| throw new NullPointerException(); |
| |
| getFacesBean().removeEntry(_COMPONENT_CHANGE_FILTERS_KEY, componentChangeFilter); |
| } |
| |
| /** |
| * Returns all the ComponentChangeFilters that are registered with this component. |
| * |
| * @return An array of registered ComponentChangeFilters |
| */ |
| public final ComponentChangeFilter[] getComponentChangeFilters() |
| { |
| Iterator<ComponentChangeFilter> filterIter = |
| (Iterator<ComponentChangeFilter>)getFacesBean().entries(_COMPONENT_CHANGE_FILTERS_KEY); |
| |
| ArrayList<ComponentChangeFilter> filterList = CollectionUtils.arrayList(filterIter); |
| return filterList.toArray(new ComponentChangeFilter[filterList.size()]); |
| } |
| |
| @Override |
| protected Iterator<UIComponent> getRenderedFacetsAndChildren( |
| FacesContext facesContext) |
| { |
| _cacheRenderer(facesContext); |
| return super.getRenderedFacetsAndChildren(facesContext); |
| } |
| |
| /** |
| * Convenience method for implementors of {@link FlattenedComponent} to setup either the |
| * visiting context or the encoding context based on if the {@link ComponentProcessingContext} |
| * is processing for encoding or not. |
| * @param facesContext The faces context |
| * @param cpContext The component processing context passed to |
| * {@link FlattenedComponent#processFlattenedChildren} |
| */ |
| protected void setupFlattenedContext( |
| FacesContext facesContext, |
| ComponentProcessingContext cpContext |
| ) |
| { |
| if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING)) |
| { |
| setupEncodingContext(facesContext, RenderingContext.getCurrentInstance()); |
| } |
| else |
| { |
| setupVisitingContext(facesContext); |
| } |
| } |
| |
| /** |
| * Convenience method for implementors of {@link FlattenedComponent} to setup either the |
| * visiting context or the encoding context based on if the {@link ComponentProcessingContext} |
| * is processing for encoding or not. |
| * @param facesContext The faces context |
| * @param cpContext The component processing context passed to |
| * {@link FlattenedComponent#processFlattenedChildren} |
| */ |
| protected void setupFlattenedChildrenContext( |
| FacesContext facesContext, |
| ComponentProcessingContext cpContext |
| ) |
| { |
| if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING)) |
| { |
| setupChildrenEncodingContext(facesContext, RenderingContext.getCurrentInstance()); |
| } |
| else |
| { |
| setupChildrenVisitingContext(facesContext); |
| } |
| } |
| |
| /** |
| * Convenience method for implementors of {@link FlattenedComponent} to tear down either the |
| * visiting context or the encoding context based on if the {@link ComponentProcessingContext} |
| * is processing for encoding or not. |
| * @param facesContext The faces context |
| * @param cpContext The component processing context passed to |
| * {@link FlattenedComponent#processFlattenedChildren} |
| */ |
| protected void tearDownFlattenedContext( |
| FacesContext facesContext, |
| ComponentProcessingContext cpContext |
| ) |
| { |
| if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING)) |
| { |
| tearDownEncodingContext(facesContext, RenderingContext.getCurrentInstance()); |
| } |
| else |
| { |
| tearDownVisitingContext(facesContext); |
| } |
| } |
| |
| /** |
| * Convenience method for implementors of {@link FlattenedComponent} to tear down either the |
| * visiting context or the encoding context based on if the {@link ComponentProcessingContext} |
| * is processing for encoding or not. |
| * @param facesContext The faces context |
| * @param cpContext The component processing context passed to |
| * {@link FlattenedComponent#processFlattenedChildren} |
| */ |
| protected void tearDownFlattenedChildrenContext( |
| FacesContext facesContext, |
| ComponentProcessingContext cpContext |
| ) |
| { |
| if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING)) |
| { |
| tearDownChildrenEncodingContext(facesContext, RenderingContext.getCurrentInstance()); |
| } |
| else |
| { |
| tearDownChildrenVisitingContext(facesContext); |
| } |
| } |
| |
| // ------------------------------------------------------------- Properties |
| |
| /** |
| * Calculates the clientId for the component |
| */ |
| private String _calculateClientId(FacesContext context) |
| { |
| // the clientId is always at least the id of the current component |
| // our implementation of getId() guarantees that this always |
| // returns a non-null value |
| String clientId = getId(); |
| |
| // Search for an ancestor that is a naming container |
| UIComponent lastParent = null; |
| UIComponent currParent = getParent(); |
| |
| while (true) |
| { |
| // prepend the NamingContainer portion of the id |
| if (currParent instanceof NamingContainer) |
| { |
| String contClientId; |
| |
| // Pass additional context information to naming containers which extend UIXComponent: |
| if (currParent instanceof UIXComponent) |
| contClientId = ((UIXComponent)currParent).getContainerClientId(context, this); |
| else |
| contClientId = currParent.getContainerClientId(context); |
| |
| StringBuilder bld = __getSharedStringBuilder(); |
| bld.append(contClientId).append(NamingContainer.SEPARATOR_CHAR).append(clientId); |
| clientId = bld.toString(); |
| break; |
| } |
| else if (currParent == null) |
| { |
| if (lastParent instanceof UIViewRoot) |
| { |
| // we got to the top of the component tree, so done looping |
| break; |
| } |
| else |
| { |
| // the component isn't in the component tree, which can cause the cached client id to be wrong. |
| |
| // =-= btsulliv see Trinidad-2374. We can't do this right now because of a couple of bogus Trinidad Renderers |
| break; |
| |
| /* |
| throw new IllegalStateException("Calling getClientId() on component " + this + |
| " when it is not in the component tree. Ancestor path:" +_getAncestorPath()); |
| */ |
| } |
| } |
| |
| lastParent = currParent; |
| currParent = lastParent.getParent(); |
| } |
| |
| Renderer renderer = getRenderer(context); |
| if (null != renderer) |
| clientId = renderer.convertClientId(context, clientId); |
| |
| return clientId; |
| } |
| |
| private List<UIComponent> _getAncestors() |
| { |
| List<UIComponent> ancestors = new ArrayList<UIComponent>(); |
| |
| UIComponent parent = getParent(); |
| |
| while (parent != null) |
| { |
| ancestors.add(parent); |
| |
| parent = parent.getParent(); |
| } |
| |
| Collections.reverse(ancestors); |
| |
| return ancestors; |
| } |
| |
| private String _getAncestorPath() |
| { |
| StringBuilder ancestorPath = new StringBuilder(1000); |
| |
| List<UIComponent> ancestors = _getAncestors(); |
| |
| if (ancestors.isEmpty()) |
| { |
| return "<none>"; |
| } |
| else |
| { |
| Iterator<UIComponent> ancestorsIter = ancestors.iterator(); |
| |
| boolean first = true; |
| |
| while (ancestorsIter.hasNext()) |
| { |
| if (!first) |
| { |
| ancestorPath.append('/'); |
| } |
| else |
| { |
| first = false; |
| } |
| |
| ancestorPath.append(ancestorsIter.next().toString()); |
| } |
| |
| return ancestorPath.toString(); |
| } |
| } |
| |
| @Override |
| public String getClientId(FacesContext context) |
| { |
| if (_isClientIdCachingEnabled()) |
| { |
| String clientId = _clientId; |
| |
| if (clientId == null) |
| { |
| // This should not be called when the parent has not been set. Should it be |
| // called, the value will not be re-calculated correctly. This will be the case |
| // if someone attempts to invoke this function during facelets view construction. |
| if (_parent == null) |
| { |
| _LOG.warning("INVALID_CALL_TO_GETCLIENTID", getId()); |
| // Return the value, even if not valid, for backward compatibility |
| return _calculateClientId(context); |
| } |
| |
| clientId = _calculateClientId(context); |
| |
| if (_usesFacesBeanImpl) |
| { |
| _clientId = clientId; |
| } |
| } |
| else if (_isClientIdDebuggingEnabled()) |
| { |
| _warnClientIdCachingConfig(context); |
| |
| // for now validate success by checking the cached result against the dynamically |
| // generated result |
| String realID = _calculateClientId(context); |
| |
| if (!clientId.equals(realID)) |
| throw new IllegalStateException( |
| String.format("Cached client id %s for %s doesn't match client id: %s", |
| clientId, this, realID)); |
| } |
| |
| return clientId; |
| } |
| else |
| { |
| _warnClientIdCachingConfig(context); |
| |
| return _calculateClientId(context); |
| } |
| } |
| |
| /** |
| * Gets the identifier for the component. This implementation |
| * never returns a null id. |
| */ |
| @Override |
| public String getId() |
| { |
| // determine whether we can use the optimized code path or not |
| if (_usesFacesBeanImpl) |
| { |
| // optimized path |
| |
| // make sure that we always have an id |
| if (_id == null) |
| { |
| FacesContext context = FacesContext.getCurrentInstance(); |
| UIViewRoot viewRoot = context.getViewRoot(); |
| |
| _id = viewRoot.createUniqueId(); |
| } |
| |
| return _id; |
| } |
| else |
| { |
| // unoptimized path |
| FacesBean facesBean = getFacesBean(); |
| |
| String id = (String)facesBean.getProperty(ID_KEY); |
| |
| // make sure that we always have an id |
| if (id == null) |
| { |
| id = FacesContext.getCurrentInstance().getViewRoot().createUniqueId(); |
| |
| facesBean.setProperty(ID_KEY, id); |
| } |
| |
| return id; |
| } |
| } |
| |
| /** |
| * Sets the identifier for the component. |
| * the identifier for the component. Every component may be named by a component identifier that must conform to the following rules: |
| * <ul> |
| * <li>They must start with a letter (as defined by the Character.isLetter() method) or underscore ( _ ).</li> |
| * <li>Subsequent characters must be letters (as defined by the Character.isLetter() method), digits as defined by the Character.isDigit() method, |
| * dashes ( - ), or underscores ( _ ). To minimize the size of responses generated by JavaServer Faces, it is recommended that component identifiers |
| * be as short as possible. If a component has been given an identifier, it must be unique in the namespace of the closest ancestor to that component |
| * that is a NamingContainer (if any). |
| * </li> |
| * </ul> |
| */ |
| @Override |
| public void setId(String id) |
| { |
| FacesBean facesBean = getFacesBean(); |
| |
| // if we are using a FacesBeanImpl, then the FacesBean will |
| // delegate all calls to set the id back to us and we can store |
| // the value localy. Otehrwise,w e need to store it in |
| // the FacesBean |
| if (_usesFacesBeanImpl) |
| { |
| // only validate if the id has actually changed |
| if ((_id == null) || !_id.equals(id)) |
| { |
| _validateId(id); |
| _id = id; |
| |
| // if we're a NamingContainer then changing our id will invalidate the clientIds of all |
| // of our children |
| if ((_clientId != null) && (this instanceof NamingContainer)) |
| { |
| clearCachedClientIds(); |
| } |
| } |
| } |
| else |
| { |
| _validateId(id); |
| facesBean.setProperty(ID_KEY, id); |
| } |
| |
| _clientId = null; |
| } |
| |
| /** |
| * Clears all of the cached clientIds in this component subtree |
| */ |
| @Override |
| public void clearCachedClientIds() |
| { |
| // clear our clientId |
| _clientId = null; |
| |
| // clear the children |
| Iterator<UIComponent> allChildren = getFacetsAndChildren(); |
| |
| while (allChildren.hasNext()) |
| { |
| clearCachedClientIds(allChildren.next()); |
| } |
| } |
| |
| @Override |
| abstract public String getFamily(); |
| |
| @Override |
| public UIComponent getParent() |
| { |
| return _parent; |
| } |
| |
| /** |
| * <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) |
| { |
| // do we add this component ? |
| if (parent != _parent) |
| { |
| if (parent != null) |
| { |
| // set the reference |
| _parent = parent; |
| |
| boolean isInView = parent.isInView(); |
| |
| _resetClientId(isInView); |
| |
| if (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. |
| _publishPostAddToViewEvent(getFacesContext(), this); |
| } |
| } |
| else |
| { |
| boolean wasInView = _parent != null && _parent.isInView(); |
| |
| if (wasInView) |
| { |
| // trigger the "remove event" lifecycle |
| // and call setInView(false) for all children/facets |
| // doing this => recursive |
| _publishPreRemoveFromViewEvent(getFacesContext(), this); |
| } |
| |
| // clear the references |
| _parent = null; |
| _resetClientId(false); |
| } |
| } |
| } |
| |
| private void _resetClientId(boolean isInView) |
| { |
| // clear cached client ids if necessary |
| if (_clientId != null) |
| { |
| String newClientId; |
| |
| // if the component is currently in the component tree, calculate the new clientId, to see if it has changed |
| if (isInView) |
| { |
| newClientId = _calculateClientId(FacesContext.getCurrentInstance()); |
| } |
| else |
| { |
| newClientId = null; |
| } |
| |
| // if our clientId changed as a result of being reparented (because we moved |
| // between NamingContainers for instance) then we need to clear out |
| boolean clearCachedIds = !_clientId.equals(newClientId); |
| |
| // all of the cached client ids for our subtree |
| if (clearCachedIds) |
| { |
| clearCachedClientIds(); |
| _clientId = newClientId; |
| } |
| } |
| } |
| |
| @Override |
| public boolean isRendered() |
| { |
| return getBooleanProperty(RENDERED_KEY, true); |
| } |
| |
| @Override |
| public void setRendered(boolean rendered) |
| { |
| setBooleanProperty(RENDERED_KEY, rendered); |
| } |
| |
| public boolean isTransient() |
| { |
| return getBooleanProperty(TRANSIENT_KEY, false); |
| } |
| |
| public void setTransient(boolean newTransient) |
| { |
| setBooleanProperty(TRANSIENT_KEY, newTransient); |
| } |
| |
| @Override |
| public String getRendererType() |
| { |
| // Don't create the FacesBean just to get the renderer type; |
| // Generally, rendererType will be the first property |
| // set, which will trigger the code below in setRendererType() |
| // to run correctly. |
| if (_facesBean == null) |
| return null; |
| |
| return (String) getProperty(RENDERER_TYPE_KEY); |
| } |
| |
| @Override |
| public void setRendererType(String rendererType) |
| { |
| String oldRendererType = getRendererType(); |
| if (oldRendererType == null) |
| { |
| if (rendererType == null) |
| return; |
| } |
| else if (oldRendererType.equals(rendererType)) |
| { |
| return; |
| } |
| |
| // prepare the faces bean |
| _init(rendererType); |
| setProperty(RENDERER_TYPE_KEY, rendererType); |
| } |
| |
| @Override |
| public boolean getRendersChildren() |
| { |
| Renderer renderer = getRenderer(getFacesContext()); |
| if (renderer == null) |
| return false; |
| |
| return renderer.getRendersChildren(); |
| } |
| |
| // ------------------------------------------------ Tree Management Methods |
| |
| @Override |
| public UIComponent findComponent(String id) |
| { |
| if (id == null) |
| throw new NullPointerException(); |
| |
| if ("".equals(id)) |
| throw new IllegalArgumentException(); |
| |
| UIComponent from = this; |
| // If it starts with the separator character, it's |
| // an absolute path: start at the top |
| if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR) |
| { |
| id = id.substring(1); |
| while (from.getParent() != null) |
| from = from.getParent(); |
| } |
| // If it's a NamingContainer, start right here |
| else if (this instanceof NamingContainer) |
| { |
| ; |
| } |
| // Otherwise, go up to look for NamingContainer (or the top) |
| else |
| { |
| while (from.getParent() != null) |
| { |
| from = from.getParent(); |
| if (from instanceof NamingContainer) |
| break; |
| } |
| } |
| |
| // Evaluate each part of the expression |
| String searchId; |
| int separatorIndex = id.indexOf(NamingContainer.SEPARATOR_CHAR); |
| if (separatorIndex < 0) |
| searchId = id; |
| else |
| searchId = id.substring(0, separatorIndex); |
| |
| if (searchId.equals(from.getId())) |
| { |
| // Don't need to look inside if we're already there |
| ; |
| } |
| else |
| { |
| from = _findInsideOf(from, searchId); |
| } |
| |
| // Final ID: break, and return whatever we've found |
| if (separatorIndex < 0) |
| { |
| return from; |
| } |
| // Just an intermediate step. Make sure we're at a NamingContainer, |
| // and then ask it to find the rest of the expression. |
| else |
| { |
| if (from == null) |
| return null; |
| |
| if (!(from instanceof NamingContainer)) |
| throw new IllegalArgumentException(); |
| return from.findComponent(id.substring(separatorIndex + 1)); |
| } |
| } |
| |
| /** |
| * <p>Create (if necessary) and return a List of the children associated |
| * with this component.</p> |
| */ |
| @Override |
| public List<UIComponent> getChildren() |
| { |
| if (_children == null) |
| _children = new ChildArrayList(this); |
| |
| return _children; |
| } |
| |
| @Override |
| public int getChildCount() |
| { |
| if (_children == null) |
| return 0; |
| else |
| return getChildren().size(); |
| } |
| |
| /** |
| * <p>Create (if necessary) and return a Map of the facets associated |
| * with this component.</p> |
| */ |
| @Override |
| public Map<String, UIComponent> getFacets() |
| { |
| |
| if (_facets == null) |
| _facets = new FacetHashMap(this); |
| |
| return _facets; |
| } |
| |
| @Override |
| public UIComponent getFacet(String facetName) |
| { |
| if (facetName == null) |
| throw new NullPointerException(); |
| |
| if (_facets == null) |
| return null; |
| else |
| return getFacets().get(facetName); |
| } |
| |
| /** |
| * Returns an Iterator over the names of all facets. |
| * Unlike getFacets().keySet().iterator(), this does |
| * not require instantiating a Map if there are |
| * no facets. (Note that this is not part of the |
| * UIComponent API.) |
| */ |
| public Iterator<String> getFacetNames() |
| { |
| if (_facets == null) |
| return _EMPTY_STRING_ITERATOR; |
| else |
| return _facets.keySet().iterator(); |
| } |
| |
| @Override |
| public Iterator<UIComponent> getFacetsAndChildren() |
| { |
| // =-=AEW Is this supposed to be an immutable Iterator? |
| if (_facets == null) |
| { |
| if (_children == null) |
| return _EMPTY_UICOMPONENT_ITERATOR; |
| |
| return _children.iterator(); |
| } |
| else |
| { |
| if (_children == null) |
| return _facets.values().iterator(); |
| } |
| |
| return new CompositeIterator<UIComponent>(_children.iterator(), _facets.values().iterator()); |
| } |
| |
| // ------------------------------------------- Event processing methods |
| |
| @Override |
| public void broadcast(FacesEvent event) |
| throws AbortProcessingException |
| { |
| if (event == null) |
| throw new NullPointerException(); |
| |
| if (_LOG.isFine()) |
| _LOG.fine("Broadcasting event " + event + " to " + this); |
| |
| UIComponent component = event.getComponent(); |
| |
| FacesContext context = getFacesContext(); |
| assert _isCurrentComponent(context, component); |
| assert _isCompositeParentCurrent(context, component); |
| |
| if (component != null && satisfiesPartialTrigger(event)) |
| { |
| RequestContext adfContext = RequestContext.getCurrentInstance(); |
| if (adfContext != null) |
| adfContext.partialUpdateNotify(component); |
| } |
| |
| Renderer renderer = getRenderer(context); |
| if (renderer instanceof CoreRenderer) |
| { |
| // Allow the renderer to handle the event |
| ((CoreRenderer)renderer).broadcast(this, event); |
| } |
| |
| Iterator<FacesListener> iter = |
| (Iterator<FacesListener>)getFacesBean().entries(_LISTENERS_KEY); |
| |
| |
| if (event instanceof BehaviorEvent) |
| { |
| BehaviorEvent behaviorEvent = (BehaviorEvent) event; |
| Behavior behavior = behaviorEvent.getBehavior(); |
| behavior.broadcast(behaviorEvent); |
| } |
| |
| |
| while (iter.hasNext()) |
| { |
| FacesListener listener = iter.next(); |
| if (event.isAppropriateListener(listener)) |
| { |
| event.processListener(listener); |
| } |
| } |
| |
| if (event instanceof AttributeChangeEvent) |
| { |
| broadcastToMethodExpression(event, getAttributeChangeListener()); |
| } |
| } |
| |
| /** |
| * Check if a faces event broadcast to this component should trigger the partial updates of the |
| * target listeners of this component. By default, all events trigger a partial update of the listeners. |
| * |
| * @param event The event to check |
| * @return true if the partial triggers should be updated by this event being broadcast |
| */ |
| protected boolean satisfiesPartialTrigger( |
| FacesEvent event) |
| { |
| return true; |
| } |
| |
| // ------------------------------------------- Lifecycle Processing Methods |
| |
| @Override |
| public void decode(FacesContext context) |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| // Find all the partialTriggers and save on the context |
| Map<String, Object> attrs = getAttributes(); |
| Object triggers = attrs.get("partialTriggers"); |
| if (triggers instanceof String[]) |
| { |
| RequestContext adfContext = RequestContext.getCurrentInstance(); |
| if (adfContext != null) |
| adfContext.addPartialTriggerListeners(this, (String[]) triggers); |
| } |
| |
| __rendererDecode(context); |
| } |
| |
| @Override |
| public void encodeBegin(FacesContext context) throws IOException |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent) |
| pushComponentToEL(context, this); |
| |
| if (!isRendered()) |
| return; |
| |
| context.getApplication().publishEvent(context, PreRenderComponentEvent.class, UIComponent.class, this); |
| |
| _cacheRenderer(context); |
| Renderer renderer = getRenderer(context); |
| |
| // if there is a Renderer for this component |
| if (renderer != null) |
| { |
| renderer.encodeBegin(context, this); |
| } |
| } |
| |
| @Override |
| public void encodeChildren(FacesContext context) throws IOException |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| if (!isRendered()) |
| return; |
| |
| Renderer renderer = getRenderer(context); |
| // if there is a Renderer for this component |
| if (renderer != null) |
| { |
| renderer.encodeChildren(context, this); |
| } |
| } |
| |
| @Override |
| public void encodeEnd(FacesContext context) throws IOException |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| try |
| { |
| if (isRendered()) |
| { |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| |
| try |
| { |
| Renderer renderer = getRenderer(context); |
| // if there is a Renderer for this component |
| if (renderer != null) |
| { |
| renderer.encodeEnd(context, this); |
| } |
| } |
| finally |
| { |
| requestContext.popCurrentComponent(context, this); |
| } |
| } |
| } |
| finally |
| { |
| popComponentFromEL(context); |
| } |
| } |
| |
| @Override |
| public void queueEvent(FacesEvent event) |
| { |
| if (event == null) |
| throw new NullPointerException(); |
| |
| UIComponent parent = getParent(); |
| if (parent == null) |
| throw new IllegalStateException(); |
| |
| parent.queueEvent(event); |
| } |
| |
| // ----------------------------------------------- Lifecycle Phase Handlers |
| |
| @Override |
| public void processDecodes(FacesContext context) |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| if (!isRendered()) |
| return; |
| |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, this); |
| |
| try |
| { |
| // Process all facets and children of this component |
| decodeChildren(context); |
| |
| // Process this component itself |
| decode(context); |
| } |
| finally |
| { |
| // Call UIComponent.popComponentFromEL(javax.faces.context.FacesContext) from inside of a finally |
| // block, just before returning. |
| |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| } |
| |
| @Override |
| public void processValidators(FacesContext context) |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| if (!isRendered()) |
| return; |
| |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, this); |
| |
| try |
| { |
| // Process all facets and children of this component |
| validateChildren(context); |
| } |
| finally |
| { |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| } |
| |
| @Override |
| public void processUpdates(FacesContext context) |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| if (!isRendered()) |
| return; |
| |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, this); |
| |
| try |
| { |
| // Process all facets and children of this component |
| updateChildren(context); |
| } |
| finally |
| { |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| } |
| |
| @Override |
| public Object processSaveState(FacesContext context) |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| if (_LOG.isFiner()) |
| _LOG.finer("processSaveState() on " + this); |
| |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, this); |
| |
| Object state = null; |
| |
| try |
| { |
| if (((_children == null) || _children.isEmpty()) && |
| ((_facets == null) || _facets.isEmpty())) |
| { |
| state = saveState(context); |
| } |
| else |
| { |
| TreeState treeState = new TreeState(); |
| treeState.saveState(context, this); |
| if (treeState.isEmpty()) |
| state = null; |
| |
| state = treeState; |
| } |
| } |
| catch (RuntimeException e) |
| { |
| _LOG.warning(_LOG.getMessage("COMPONENT_CHILDREN_SAVED_STATE_FAILED", this)); |
| |
| throw e; |
| } |
| |
| finally |
| { |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| |
| return state; |
| } |
| |
| // TODO will have deep problems if UIComponent.saveState() ever |
| // returns a String. |
| // TODO crashes and burns if there are fewer children or missing |
| // facets from when state was saved. |
| @Override |
| public void processRestoreState(FacesContext context, Object state) |
| { |
| if (context == null) |
| throw new NullPointerException(); |
| |
| if (_LOG.isFiner()) |
| _LOG.finer("processRestoreState() on " + this); |
| |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, this); |
| |
| try |
| { |
| // If we saved a "TreeState", use it to restore everything |
| if (state instanceof TreeState) |
| { |
| ((TreeState) state).restoreState(context, this); |
| } |
| // Otherwise, we had no children or facets, and just use |
| // the "state" object |
| else |
| { |
| restoreState(context, state); |
| } |
| } |
| finally |
| { |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| } |
| |
| @Override |
| public void markInitialState() |
| { |
| // -= Simon Lessard =- |
| // FIXME: Set to true, but never read |
| //_initialStateMarked = true; |
| getFacesBean().markInitialState(); |
| } |
| |
| @Override |
| public void clearInitialState() |
| { |
| getFacesBean().clearInitialState(); |
| } |
| |
| @Override |
| public boolean initialStateMarked() |
| { |
| return getFacesBean().initialStateMarked(); |
| } |
| |
| public Object saveState(FacesContext facesContext) |
| { |
| Object state = getFacesBean().saveState(facesContext); |
| |
| // if component state serialization checking is on, attempt to Serialize the |
| // component state immediately in order to determine which component's state |
| // failed state saving. |
| if (StateUtils.checkComponentStateSerialization(facesContext)) |
| { |
| try |
| { |
| new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(state); |
| } |
| catch (IOException e) |
| { |
| throw new RuntimeException(_LOG.getMessage("COMPONENT_SAVED_STATE_FAILED", this), e); |
| } |
| } |
| |
| return state; |
| } |
| |
| public void restoreState( |
| FacesContext facesContext, |
| Object stateObj) |
| { |
| getFacesBean().restoreState(facesContext, stateObj); |
| } |
| |
| @Override |
| public String toString() |
| { |
| String className = getClass().getName(); |
| int periodIndex = className.lastIndexOf('.'); |
| if (periodIndex >= 0) |
| className = className.substring(periodIndex + 1); |
| |
| return className + "[" + getFacesBean().toString() + ", id=" + getId() + "]"; |
| } |
| |
| /** |
| * <p>Return the {@link FacesContext} instance for the current request.</p> |
| */ |
| @Override |
| protected FacesContext getFacesContext() |
| { |
| // If we ever have a way for a component to get notified |
| // when it's finished being used for a given request, |
| // we could cache this as an instance variable. |
| return FacesContext.getCurrentInstance(); |
| } |
| |
| /** |
| * Delegates to LifecycleRenderer, if present, |
| * otherwise calls decodeChildrenImpl. |
| * |
| * @param context the current FacesContext |
| */ |
| final protected void decodeChildren(FacesContext context) |
| { |
| LifecycleRenderer renderer = getLifecycleRenderer(context); |
| // if there is a HierarchyRenderer for this component |
| if (renderer != null) |
| { |
| if (renderer.decodeChildren(context, this)) |
| return; |
| } |
| |
| decodeChildrenImpl(context); |
| } |
| |
| /** |
| * Calls processDecodes on all facets and children of this |
| * component. |
| * @param context the current FacesContext |
| */ |
| protected void decodeChildrenImpl(FacesContext context) |
| { |
| Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context); |
| while (kids.hasNext()) |
| { |
| UIComponent kid = kids.next(); |
| kid.processDecodes(context); |
| } |
| } |
| |
| /** |
| * Delegates to LifecycleRenderer, if present, |
| * otherwise calls validateChildrenImpl. |
| * |
| * @param context the current FacesContext |
| */ |
| final protected void validateChildren(FacesContext context) |
| { |
| LifecycleRenderer renderer = getLifecycleRenderer(context); |
| // if there is a ExtendedRenderer for this component |
| if (renderer != null) |
| { |
| if (renderer.validateChildren(context, this)) |
| return; |
| } |
| |
| validateChildrenImpl(context); |
| } |
| |
| /** |
| * Calls processValidators on all facets and children of this |
| * component. |
| * @param context the current FacesContext |
| */ |
| protected void validateChildrenImpl(FacesContext context) |
| { |
| // Process all the facets and children of this component |
| Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context); |
| while (kids.hasNext()) |
| { |
| UIComponent kid = kids.next(); |
| kid.processValidators(context); |
| } |
| } |
| |
| /** |
| * Delegates to LifecycleRenderer, if present, |
| * otherwise calls upateChildrenImpl. |
| * |
| * @param context the current FacesContext |
| */ |
| final protected void updateChildren(FacesContext context) |
| { |
| LifecycleRenderer renderer = getLifecycleRenderer(context); |
| // if there is a ExtendedRenderer for this component |
| if (renderer != null) |
| { |
| if (renderer.updateChildren(context, this)) |
| return; |
| } |
| |
| updateChildrenImpl(context); |
| } |
| |
| protected void updateChildrenImpl(FacesContext context) |
| { |
| // Process all the facets and children of this component |
| Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context); |
| while (kids.hasNext()) |
| { |
| UIComponent kid = kids.next(); |
| kid.processUpdates(context); |
| } |
| } |
| |
| @Override |
| protected void addFacesListener(FacesListener listener) |
| { |
| if (listener == null) |
| throw new NullPointerException(); |
| |
| getFacesBean().addEntry(_LISTENERS_KEY, listener); |
| } |
| |
| @Override |
| protected void removeFacesListener(FacesListener listener) |
| { |
| if (listener == null) |
| throw new NullPointerException(); |
| |
| getFacesBean().removeEntry(_LISTENERS_KEY, listener); |
| } |
| |
| @Override |
| protected FacesListener[] getFacesListeners(Class clazz) |
| { |
| if (clazz == null) |
| throw new NullPointerException(); |
| |
| if (!FacesListener.class.isAssignableFrom(clazz)) |
| throw new IllegalArgumentException(); |
| |
| return (FacesListener[]) |
| getFacesBean().getEntries(_LISTENERS_KEY, clazz); |
| } |
| |
| /** |
| * Checks if any of the ComponentChangeFilter instances that is attached to this component rejects the supplied |
| * change for the supplied component. |
| */ |
| private boolean _isAnyFilterRejectingChange(UIComponent uic, ComponentChange cc) |
| { |
| // assume we accept the change |
| boolean rejectsChange = false; |
| |
| Iterator<ComponentChangeFilter> iter = |
| (Iterator<ComponentChangeFilter>)getFacesBean().entries(_COMPONENT_CHANGE_FILTERS_KEY); |
| |
| while (iter.hasNext()) |
| { |
| ComponentChangeFilter currentFilter = iter.next(); |
| if (currentFilter.accept(cc, uic) == ComponentChangeFilter.Result.REJECT) |
| { |
| // one of the filter rejected the change, look no further |
| rejectsChange = true; |
| break; |
| } |
| } |
| |
| return rejectsChange; |
| } |
| |
| private UIXComponentBase _getNextUIXComponentBaseAnxcestor() |
| { |
| UIComponent parent = getParent(); |
| |
| while (parent != null) |
| { |
| if (parent instanceof UIXComponentBase) |
| { |
| return (UIXComponentBase)parent; |
| } |
| |
| parent = parent.getParent(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Called when adding a change to a Component, or the Component's subtree. |
| * The default implementation delegates the call to the parent, if possible, otherwise |
| * it adds the change to the ChangeManager directly. |
| * Subclasses can override this method to among other things, filter or transform the changes. |
| * @param component The component that the change is for |
| * @param change The change to add for this component |
| * @return The ComponentChange actually added, or |
| * <code>null</code> if no change was added. |
| * @see #addComponentChange(ComponentChange) |
| * @see #addAttributeChange |
| */ |
| protected ComponentChange addComponentChange(UIComponent component, ComponentChange change) |
| { |
| // check moved from addAttributeChange(), as this is more central |
| if ((component == this) && (change instanceof AttributeComponentChange)) |
| { |
| AttributeComponentChange aa = (AttributeComponentChange)change; |
| Object attributeValue = aa.getAttributeValue(); |
| |
| if (attributeValue instanceof RowKeySet) |
| { |
| change = new RowKeySetAttributeChange(getClientId(getFacesContext()), |
| aa.getAttributeName(), |
| attributeValue); |
| } |
| } |
| |
| // add the change unless we have a change filter that is attached to this component wants to supress the change |
| if (!_isAnyFilterRejectingChange(component, change)) |
| { |
| UIXComponentBase nextUIXParent = _getNextUIXComponentBaseAnxcestor(); |
| |
| if (nextUIXParent != null) |
| { |
| return nextUIXParent.addComponentChange(component, change); |
| } |
| else |
| { |
| RequestContext trinContext = RequestContext.getCurrentInstance(); |
| trinContext.getChangeManager().addComponentChange(getFacesContext(), component, change); |
| return change; |
| } |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| |
| /** |
| * Convenience function for |
| * <code>addComponentChange(new AttributeComponentChange(attributeName, attributeValue));</code> |
| * This function is not <code>final</code> for backwards compatibility reasons, however, |
| * existing subclassers whould override <code>addComponentChange</code> instead. |
| * @param attributeName |
| * @param attributeValue |
| * @see #addComponentChange(UIComponent, ComponentChange) |
| */ |
| protected void addAttributeChange( |
| String attributeName, |
| Object attributeValue) |
| { |
| addComponentChange(new AttributeComponentChange(attributeName, attributeValue)); |
| } |
| |
| void __rendererDecode(FacesContext context) |
| { |
| _cacheRenderer(context); |
| Renderer renderer = getRenderer(context); |
| // if there is a Renderer for this component |
| if (renderer != null) |
| { |
| renderer.decode(context, this); |
| } |
| } |
| |
| /** |
| * Publish PostAddToViewEvent to the component and all facets and children. |
| * |
| * @param context the current FacesContext |
| * @param component the current UIComponent |
| */ |
| private void _publishPostAddToViewEvent( |
| FacesContext context, |
| UIComponent component) |
| { |
| component.setInView(true); |
| context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIComponent.class, component); |
| |
| if (component.getChildCount() > 0) |
| { |
| List<UIComponent> children = component.getChildren(); |
| UIComponent child = null; |
| UIComponent currentChild = null; |
| int i = 0; |
| while (i < children.size()) |
| { |
| child = children.get(i); |
| |
| // Iterate over the same index if the component was removed |
| // This prevents skip components when processing |
| do |
| { |
| _publishPostAddToViewEvent(context, child); |
| currentChild = child; |
| } |
| while ((i < children.size()) && |
| ((child = children.get(i)) != currentChild) ); |
| i++; |
| } |
| } |
| |
| if (component.getFacetCount() > 0) |
| { |
| for (UIComponent child : component.getFacets().values()) |
| { |
| _publishPostAddToViewEvent(context, child); |
| } |
| } |
| } |
| |
| /** |
| * Publish PreRemoveFromViewEvent to the component and all facets and children. |
| * |
| * @param context the current FacesContext |
| * @param component the current UIComponent |
| */ |
| private void _publishPreRemoveFromViewEvent( |
| FacesContext context, |
| UIComponent component) |
| { |
| context.getApplication().publishEvent(context, PreRemoveFromViewEvent.class, UIComponent.class, component); |
| |
| if (component.getChildCount() > 0) |
| { |
| for (UIComponent child : component.getChildren()) |
| { |
| _publishPreRemoveFromViewEvent(context, child); |
| } |
| } |
| |
| if (component.getFacetCount() > 0) |
| { |
| for (UIComponent child : component.getFacets().values()) |
| { |
| _publishPreRemoveFromViewEvent(context, child); |
| } |
| } |
| |
| component.setInView(false); |
| } |
| |
| private void _cacheRenderer(FacesContext context) |
| { |
| Renderer renderer = _getRendererImpl(context); |
| _cachedRenderer = renderer; |
| |
| // cache the lifecycle renderer |
| if (renderer instanceof LifecycleRenderer) |
| { |
| _cachedLifecycleRenderer = (LifecycleRenderer)renderer; |
| } |
| else |
| { |
| _cachedLifecycleRenderer = null; |
| } |
| } |
| |
| private Renderer _getRendererImpl(FacesContext context) |
| { |
| String rendererType = getRendererType(); |
| if (rendererType != null) |
| { |
| RenderKit renderKit = context.getRenderKit(); |
| Renderer renderer = renderKit.getRenderer(getFamily(), rendererType); |
| if (renderer == null) |
| { |
| _LOG.warning("CANNOT_FIND_RENDERER", new Object[]{this, rendererType}); |
| } |
| |
| return renderer; |
| } |
| |
| return null; |
| } |
| |
| private LifecycleRenderer _getLifecycleRendererImpl(FacesContext context) |
| { |
| Renderer renderer = _getRendererImpl(context); |
| if (renderer instanceof LifecycleRenderer) |
| { |
| return (LifecycleRenderer)renderer; |
| } |
| |
| return null; |
| } |
| |
| private boolean _isCurrentComponent(FacesContext context, UIComponent component) |
| { |
| return component.equals(UIComponent.getCurrentComponent(context)); |
| } |
| |
| private boolean _isCompositeParentCurrent(FacesContext context, UIComponent component) |
| { |
| UIComponent currentCompositeComponent = UIComponent.getCurrentCompositeComponent(context); |
| if(UIComponent.isCompositeComponent(component)) |
| { |
| return component.equals(currentCompositeComponent); |
| } |
| else |
| { |
| UIComponent compositeParent = UIComponent.getCompositeComponentParent(component); |
| if(compositeParent == null) |
| { |
| return currentCompositeComponent == null; |
| } |
| else |
| { |
| return compositeParent.equals(currentCompositeComponent); |
| } |
| } |
| } |
| |
| @Override |
| protected Renderer getRenderer(FacesContext context) |
| { |
| Renderer renderer = _cachedRenderer; |
| if (renderer != _UNDEFINED_RENDERER) |
| return renderer; |
| |
| return _getRendererImpl(context); |
| } |
| |
| protected LifecycleRenderer getLifecycleRenderer(FacesContext context) |
| { |
| LifecycleRenderer renderer = _cachedLifecycleRenderer; |
| if (renderer != _UNDEFINED_LIFECYCLE_RENDERER) |
| return renderer; |
| |
| return _getLifecycleRendererImpl(context); |
| |
| } |
| |
| protected void setProperty(PropertyKey key, Object value) |
| { |
| getFacesBean().setProperty(key, value); |
| } |
| |
| protected Object getProperty(PropertyKey key) |
| { |
| return getFacesBean().getProperty(key); |
| } |
| |
| protected void setBooleanProperty(PropertyKey key, boolean value) |
| { |
| getFacesBean().setProperty(key, value ? Boolean.TRUE : Boolean.FALSE); |
| } |
| |
| protected boolean getBooleanProperty(PropertyKey key, boolean defaultValue) |
| { |
| Object o = getFacesBean().getProperty(key); |
| if (defaultValue) |
| return !Boolean.FALSE.equals(o); |
| else |
| return Boolean.TRUE.equals(o); |
| } |
| |
| protected void setIntProperty(PropertyKey key, int value) |
| { |
| getFacesBean().setProperty(key, Integer.valueOf(value)); |
| } |
| |
| protected int getIntProperty(PropertyKey key, int defaultValue) |
| { |
| Number n = (Number) getFacesBean().getProperty(key); |
| if (n == null) |
| return defaultValue; |
| |
| return n.intValue(); |
| } |
| |
| /** |
| * Return the number of facets. This is more efficient than |
| * calling getFacets().size(); |
| */ |
| @Override |
| public int getFacetCount() |
| { |
| if (_facets == null) |
| return 0; |
| |
| return _facets.size(); |
| } |
| |
| /** |
| * Broadcast an event to a MethodBinding. |
| * This can be used to support MethodBindings such as the "actionListener" |
| * binding on ActionSource components: |
| * <tr:commandButton actionListener="#{mybean.myActionListener}"> |
| * @deprecated |
| */ |
| protected final void broadcastToMethodBinding( |
| FacesEvent event, |
| MethodBinding method) throws AbortProcessingException |
| { |
| if (method != null) |
| { |
| try |
| { |
| FacesContext context = getFacesContext(); |
| method.invoke(context, new Object[] { event }); |
| } |
| catch (EvaluationException ee) |
| { |
| // Checking for AbortProcessingExceptions, and unwrapping |
| // it if the underlying exception is AbortProcessingExceptions. |
| Throwable currentThrowable = ee.getCause(); |
| while (currentThrowable != null) |
| { |
| if (currentThrowable instanceof AbortProcessingException) |
| { |
| throw ((AbortProcessingException)currentThrowable); |
| } |
| currentThrowable = currentThrowable.getCause(); |
| } |
| throw ee; |
| } |
| } |
| } |
| |
| /** |
| * Given a MethodBinding, create a MethodExpression that |
| * adapts it. |
| */ |
| static public MethodExpression adaptMethodBinding(MethodBinding binding) |
| { |
| return new MethodBindingMethodExpression(binding); |
| } |
| |
| /** |
| * Broadcast an event to a MethodExpression. |
| * This can be used to support MethodBindings such as the "actionListener" |
| * binding on ActionSource components: |
| * <tr:commandButton actionListener="#{mybean.myActionListener}"> |
| */ |
| protected final void broadcastToMethodExpression( |
| FacesEvent event, |
| MethodExpression method) throws AbortProcessingException |
| { |
| if (method != null) |
| { |
| try |
| { |
| FacesContext context = getFacesContext(); |
| method.invoke(context.getELContext(), new Object[] { event }); |
| } |
| catch (ELException ee) |
| { |
| Throwable t = ee.getCause(); |
| // Unwrap AbortProcessingExceptions |
| if (t instanceof AbortProcessingException) |
| throw ((AbortProcessingException) t); |
| throw ee; |
| } |
| } |
| } |
| |
| /** |
| * Convenience method to call <code>invokeOnComponent</code> on all of the |
| * children of a component, surrounding the invocation with calls to |
| * <code>setup/tearDownChildrenVisitingContext</code>. |
| * This is useful when a component sometimes optimizes |
| * away calling <code>invokeOnComponent</code> on its children. |
| * @see UIXComponent#setupChildrenVisitingContext |
| * @see UIXComponent#tearDownChildrenVisitingContext |
| */ |
| protected final boolean invokeOnChildrenComponents( |
| FacesContext context, |
| String clientId, |
| ContextCallback callback) |
| throws FacesException |
| { |
| setupChildrenVisitingContext(context); |
| |
| boolean found = false; |
| |
| try |
| { |
| Iterator<UIComponent> children = getFacetsAndChildren(); |
| |
| while (children.hasNext() && !found) |
| { |
| found = children.next().invokeOnComponent(context, clientId, callback); |
| } |
| } |
| finally |
| { |
| tearDownChildrenVisitingContext(context); |
| } |
| |
| return found; |
| } |
| |
| /** |
| * <p> |
| * Optimized implementation of <code>invokeOnComponent</code> for NamingContainers. |
| * If the clientId isn't within the NamingContainer, invocation of the |
| * NamingContainer's children is skipped. |
| * </p> |
| * <p>Subclasses implementing NamingContainer should override |
| * <code>invokeOnComponent</code> and delegate to this method.</p> |
| */ |
| protected final boolean invokeOnNamingContainerComponent( |
| FacesContext context, |
| String clientId, |
| ContextCallback callback) |
| throws FacesException |
| { |
| assert this instanceof NamingContainer : "Only use invokeOnNamingContainerComponent on NamingContainers"; |
| |
| boolean invokedComponent; |
| |
| setupVisitingContext(context); |
| |
| try |
| { |
| String thisClientId = getClientId(context); |
| |
| if (clientId.equals(thisClientId)) |
| { |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, null); |
| |
| try |
| { |
| // this is the component we want, so invoke the callback |
| callback.invokeContextCallback(context, this); |
| } |
| finally |
| { |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| |
| invokedComponent = true; |
| } |
| else |
| { |
| // if this is a NamingContainer, only traverse into it if the clientId we are looking for |
| // is inside of it |
| if ((!clientId.startsWith(thisClientId) || |
| (clientId.charAt(thisClientId.length()) != NamingContainer.SEPARATOR_CHAR))) |
| { |
| invokedComponent = false; |
| } |
| else |
| { |
| // iterate through children. |
| // We inline this code instead of calling super in order |
| // to avoid making an extra call to getClientId(). |
| invokedComponent = invokeOnChildrenComponents(context, clientId, callback); |
| } |
| } |
| } |
| finally |
| { |
| // teardown the context now that we have visited the children |
| tearDownVisitingContext(context); |
| } |
| |
| return invokedComponent; |
| } |
| |
| /** |
| * Override to calls the hooks for setting up and tearing down the |
| * context before the children are visited. |
| * @see #setupVisitingContext |
| * @see #tearDownVisitingContext |
| */ |
| @Override |
| public boolean invokeOnComponent( |
| FacesContext context, |
| String clientId, |
| ContextCallback callback) |
| throws FacesException |
| { |
| boolean invokedComponent; |
| |
| // set up the context for visiting the children |
| setupVisitingContext(context); |
| |
| try |
| { |
| String thisClientId = getClientId(context); |
| |
| if (clientId.equals(thisClientId)) |
| { |
| // push component to the stack before invoking the component. |
| RequestContext requestContext = RequestContext.getCurrentInstance(); |
| requestContext.pushCurrentComponent(context, this); |
| pushComponentToEL(context, null); |
| |
| try |
| { |
| // this is the component we want, so invoke the callback |
| callback.invokeContextCallback(context, this); |
| } |
| finally |
| { |
| popComponentFromEL(context); |
| requestContext.popCurrentComponent(context, this); |
| } |
| |
| // we found the component |
| invokedComponent = true; |
| } |
| else |
| { |
| // set up the children visiting context to iterate through children. We inline this |
| // code instead of calling super in order |
| // to avoid making an extra call to getClientId(). |
| invokedComponent = invokeOnChildrenComponents(context, clientId, callback); |
| } |
| } |
| finally |
| { |
| // teardown the context now that we have visited the component |
| tearDownVisitingContext(context); |
| } |
| |
| return invokedComponent; |
| } |
| |
| |
| @Override |
| public void subscribeToEvent(Class<? extends SystemEvent> eventClass, ComponentSystemEventListener componentListener) |
| { |
| if (eventClass == null) |
| { |
| throw new NullPointerException("eventClass required"); |
| } |
| if (componentListener == null) |
| { |
| throw new NullPointerException("componentListener required"); |
| } |
| |
| FacesBean bean = getFacesBean(); |
| |
| AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage = |
| (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY); |
| |
| if (eventStorage == null) |
| { |
| eventStorage = new AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>(); |
| bean.setProperty(_SYSTEM_EVENT_LISTENERS_KEY, eventStorage); |
| } |
| |
| if (componentListener instanceof SystemEventListener && componentListener instanceof StateHolder) |
| eventStorage.addAttachedObject(eventClass, (SystemEventListener) componentListener); |
| else |
| eventStorage.addAttachedObject(eventClass, new ComponentSystemEventListenerWrapper(componentListener, this)); |
| } |
| |
| @Override |
| public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass, |
| ComponentSystemEventListener componentListener) |
| { |
| if (eventClass == null) |
| { |
| throw new NullPointerException("eventClass required"); |
| } |
| if (componentListener == null) |
| { |
| throw new NullPointerException("componentListener required"); |
| } |
| |
| FacesBean bean = getFacesBean(); |
| |
| AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage = |
| (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY); |
| |
| if (eventStorage == null) |
| { |
| return; |
| } |
| |
| if (componentListener instanceof SystemEventListener && componentListener instanceof StateHolder) |
| { |
| eventStorage.removeAttachedObject(eventClass, (SystemEventListener) componentListener); |
| } |
| else |
| { |
| // ComponentSystemEventListenerWrapper implements equals() to compare listener and component |
| eventStorage.removeAttachedObject(eventClass, new ComponentSystemEventListenerWrapper(componentListener, this)); |
| } |
| } |
| |
| @Override |
| public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass) |
| { |
| FacesBean bean = getFacesBean(); |
| |
| AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage = |
| (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY); |
| |
| if (eventStorage == null) |
| { |
| return Collections.emptyList(); |
| } |
| |
| return eventStorage.getAttachedObjectList(eventClass); |
| } |
| |
| 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 && create) |
| {//_LOG.severe("create new passTA", new Exception()); |
| _passthroughAttributesMap = new PassThroughAttributesMap(getFacesBean()); |
| } |
| return _passthroughAttributesMap; |
| } |
| |
| // ------------------------- Client behavior holder methods ------------------------- |
| |
| /** |
| * Utility method to assist sub-classes in the implementation of the |
| * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface. |
| * <p>This method must only |
| * be called by classes that implement the interface, doing otherwise will result in an exception. |
| * </p> |
| * @param eventName The event name |
| * @param behavior The behavior to add |
| * @see javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior(String, ClientBehavior) |
| */ |
| protected void addClientBehavior( |
| String eventName, |
| ClientBehavior behavior) |
| { |
| // This will throw a class cast exception when illegally called by a class that does not |
| // implement ClientBehaviorHolder |
| Collection<String> events = ((ClientBehaviorHolder)this).getEventNames(); |
| |
| // This will throw a null pointer exception if the component author did not correctly implement |
| // the ClientBehaviorHolder contract which requires a non-empty collection to be returned from |
| // getEventNames |
| if (!events.contains(eventName)) |
| { |
| return; |
| } |
| |
| FacesBean bean = getFacesBean(); |
| |
| AttachedObjects<String, ClientBehavior> behaviors = ( |
| AttachedObjects<String, ClientBehavior>)bean.getProperty(_CLIENT_BEHAVIORS_KEY); |
| |
| if (behaviors == null) |
| { |
| behaviors = new AttachedObjects<String, ClientBehavior>(); |
| bean.setProperty(_CLIENT_BEHAVIORS_KEY, behaviors); |
| } |
| |
| behaviors.addAttachedObject(eventName, behavior); |
| } |
| |
| // Note, we do not need to provide a default implementation for the event names, as client |
| // behavior holder components must provide a non-empty list of event names. UIComponentBase |
| // decided to return a non-valid null in their code, but that is only confusing to the user, it |
| // is better to not implement the method and force the users to write the method upon interface |
| // implementation. |
| //protected Collection<String> getEventNames() {} |
| |
| /** |
| * Utility method to assist sub-classes in the implementation of the |
| * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface. |
| * <p>This method must only |
| * be called by classes that implement the interface, doing otherwise will result in an exception. |
| * </p> |
| * @see javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors() |
| * @return Read-only map of the client behaviors for this component |
| */ |
| protected Map<String, List<ClientBehavior>> getClientBehaviors() |
| { |
| AttachedObjects<String, ClientBehavior> behaviors = ( |
| AttachedObjects<String, ClientBehavior>)getFacesBean().getProperty(_CLIENT_BEHAVIORS_KEY); |
| |
| if (behaviors == null) |
| { |
| return Collections.emptyMap(); |
| } |
| |
| return behaviors.getAttachedObjectMap(); |
| } |
| |
| /** |
| * Utility method to assist sub-classes in the implementation of the |
| * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface. |
| * <p>This method must only |
| * be called by classes that implement the interface, doing otherwise will result in an exception. |
| * </p> |
| * @return null |
| * @see javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName() |
| */ |
| protected String getDefaultEventName() |
| { |
| _ensureClientBehaviorHolder(); |
| return null; |
| } |
| |
| private void _ensureClientBehaviorHolder() |
| { |
| if (!(this instanceof ClientBehaviorHolder)) |
| { |
| throw new IllegalStateException("Component must implement ClientBehaviorHolder in order " + |
| "to make use of this method."); |
| } |
| } |
| // ------------------------- End of the client behavior holder methods ------------------------- |
| |
| /** |
| * <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; |
| } |
| |
| /** |
| * render a component. this is called by renderers whose |
| * getRendersChildren() return true. |
| * @deprecated {@link UIComponent#encodeAll(FacesContext)} should be used instead of this method |
| */ |
| @Deprecated |
| void __encodeRecursive(FacesContext context, UIComponent component) |
| throws IOException |
| { |
| component.encodeAll(context); |
| } |
| |
| static private UIComponent _findInsideOf( |
| UIComponent from, |
| String id) |
| { |
| Iterator<UIComponent> kids = from.getFacetsAndChildren(); |
| while (kids.hasNext()) |
| { |
| UIComponent kid = kids.next(); |
| if (id.equals(kid.getId())) |
| return kid; |
| |
| if (!(kid instanceof NamingContainer)) |
| { |
| UIComponent returned = _findInsideOf(kid, id); |
| if (returned != null) |
| return returned; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * <p>Verify that the specified component id is safe to add to the tree. |
| * </p> |
| * |
| * @param id The proposed component id to check for validity |
| * |
| * @exception IllegalArgumentException if <code>id</code> |
| * is <code>null</code> or contains invalid characters |
| */ |
| private void _validateId(String id) |
| { |
| if (id == null) |
| return; |
| |
| |
| int n = id.length(); |
| if (0 == n || |
| NamingContainer.SEPARATOR_CHAR == id.charAt(0)) |
| _throwBadId(id); |
| |
| for (int i = 0; i < n; i++) |
| { |
| char c = id.charAt(i); |
| if (i == 0) |
| { |
| if (!Character.isLetter(c) && (c != '_')) |
| _throwBadId(id); |
| } |
| else |
| { |
| if (!(Character.isLetter(c) || |
| Character.isDigit(c) || |
| (c == '-') || (c == '_'))) |
| { |
| _throwBadId(id); |
| } |
| } |
| } |
| } |
| |
| private void _throwBadId(String id) |
| { |
| throw new IllegalArgumentException(_LOG.getMessage( |
| "ILLEGAL_ID", id)); |
| } |
| |
| private void _init( |
| String rendererType) |
| { |
| FacesBean oldBean = _facesBean; |
| FacesBean newBean = createFacesBean(rendererType);; |
| |
| if (oldBean != null) |
| newBean.addAll(oldBean); |
| |
| _attributes = new ValueMap(newBean); |
| |
| _facesBean = newBean; |
| |
| // determine whether it is ok to store the attributes locally. We cache the result since |
| // this can be a little involved |
| boolean usesFacesBeanImpl = false; |
| |
| if (newBean instanceof UIXFacesBeanImpl) |
| usesFacesBeanImpl = true; |
| else |
| { |
| // handle the wrapped case |
| FacesBean currImpl = newBean; |
| |
| while (currImpl instanceof FacesBeanWrapper) |
| { |
| currImpl = ((FacesBeanWrapper)currImpl).getWrappedBean(); |
| |
| if (currImpl instanceof UIXFacesBeanImpl) |
| { |
| usesFacesBeanImpl = true; |
| break; |
| } |
| } |
| } |
| |
| _usesFacesBeanImpl = usesFacesBeanImpl; |
| } |
| |
| private FacesBean _facesBean; |
| private List<UIComponent> _children; |
| private Map<String, Object> _attributes; |
| private Map<String, UIComponent> _facets; |
| private UIComponent _parent; |
| private String _id; |
| private String _clientId; |
| private boolean _usesFacesBeanImpl; |
| private Map<String, Object> _passthroughAttributesMap; |
| |
| // Cached instance of the Renderer for this component. |
| // The instance will be re-retrieved in encodeBegin() |
| private transient Renderer _cachedRenderer = _UNDEFINED_RENDERER; |
| private transient LifecycleRenderer _cachedLifecycleRenderer = |
| _UNDEFINED_LIFECYCLE_RENDERER; |
| |
| // -= Simon Lessard =- |
| // FIXME: _initialStateMarked is never read |
| // So commented out, is that ok? If so, this attribute should be deleted |
| //private transient boolean _initialStateMarked; |
| |
| private static final Iterator<String> _EMPTY_STRING_ITERATOR = CollectionUtils.emptyIterator(); |
| private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR = |
| CollectionUtils.emptyIterator(); |
| |
| static private final ThreadLocal<StringBuilder> _STRING_BUILDER = |
| ThreadLocalUtils.newRequestThreadLocal(); |
| |
| static private FacesBean.Type _createType() |
| { |
| try |
| { |
| ClassLoader cl = _getClassLoader(); |
| URL url = cl.getResource("META-INF/faces-bean-type.properties"); |
| if (url != null) |
| { |
| Properties properties = new Properties(); |
| InputStream is = url.openStream(); |
| try |
| { |
| properties.load(is); |
| String className = (String) |
| properties.get(UIXComponentBase.class.getName()); |
| return (FacesBean.Type) cl.loadClass(className).newInstance(); |
| } |
| finally |
| { |
| is.close(); |
| } |
| } |
| } |
| catch (Exception e) |
| { |
| _LOG.severe("CANNOT_LOAD_TYPE_PROPERTIES", e); |
| } |
| |
| // For testing purposes, return a valid Type |
| return new FacesBean.Type(); |
| } |
| |
| static private ClassLoader _getClassLoader() |
| { |
| ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| if (loader == null) |
| loader = FacesBeanFactory.class.getClassLoader(); |
| return loader; |
| } |
| |
| static private class RendererImpl extends Renderer |
| { |
| } |
| |
| static private class ExtendedRendererImpl extends ExtendedRenderer |
| { |
| } |
| |
| private static boolean _isClientIdCachingEnabled() |
| { |
| return (_CLIENT_ID_CACHING != ClientIdCaching.OFF); |
| } |
| |
| private static boolean _isClientIdDebuggingEnabled() |
| { |
| return (_CLIENT_ID_CACHING == ClientIdCaching.DEBUG); |
| } |
| |
| // Warn if caching is disabled + production environment since this is |
| // undesirable from a performance perspective. |
| private static void _warnClientIdCachingConfig(FacesContext context) |
| { |
| if (_CLIENT_ID_CACHING != ClientIdCaching.ON && |
| context.isProjectStage(ProjectStage.Production) && |
| !_warnedClientIdCachingConfig(context)) |
| { |
| _LOG.warning( |
| "The org.apache.myfaces.trinidad.CLIENT_ID_CACHING system property is set to: " + |
| _CLIENT_ID_CACHING + |
| ". For best performance, client id caching should be ON in production environments."); |
| |
| _clientIdCachingConfigWarned(context); |
| } |
| } |
| |
| // Tests whether we have already warned about the caching config. |
| // We only want to warn once per run. |
| private static boolean _warnedClientIdCachingConfig(FacesContext context) |
| { |
| ExternalContext external = context.getExternalContext(); |
| Map<String, Object> appMap = external.getApplicationMap(); |
| |
| return Boolean.TRUE.equals(appMap.get(_WARNED_CLIENT_ID_CACHING_KEY)); |
| } |
| |
| // Marks the fact that we have now warned about the caching config. |
| private static void _clientIdCachingConfigWarned(FacesContext context) |
| { |
| ExternalContext external = context.getExternalContext(); |
| Map<String, Object> appMap = external.getApplicationMap(); |
| |
| appMap.put(_WARNED_CLIENT_ID_CACHING_KEY, Boolean.TRUE); |
| } |
| |
| // Utility for deriving initial CLIENT_ID_CACHING value. |
| private static ClientIdCaching _initClientIdCaching() |
| { |
| String cachingProperty = _getClientIdCachingSystemProperty(); |
| ClientIdCaching caching = _toClientIdCachingEnum(cachingProperty); |
| |
| _LOG.config("Client id caching configuration: " + caching); |
| |
| return caching; |
| } |
| |
| private static String _getClientIdCachingSystemProperty() |
| { |
| try |
| { |
| return System.getProperty(_SYSTEM_PROP_CLIENT_ID_CACHING); |
| } |
| catch (Throwable t) |
| { |
| _LOG.warning(t); |
| } |
| |
| return null; |
| } |
| |
| private static ClientIdCaching _toClientIdCachingEnum(String cachingProperty) |
| { |
| try |
| { |
| return Enum.valueOf(ClientIdCaching.class, |
| (cachingProperty == null) ? |
| "ON" : |
| cachingProperty.toUpperCase()); |
| } |
| catch (IllegalArgumentException e) |
| { |
| _LOG.warning("Invalid value specified for " + |
| _SYSTEM_PROP_CLIENT_ID_CACHING + |
| " system property: " + |
| cachingProperty + |
| ". Valid values are: on | off | debug."); |
| |
| } |
| |
| return ClientIdCaching.ON; |
| } |
| |
| // Little enum for tracking the client id caching behavior. |
| private enum ClientIdCaching |
| { |
| ON, |
| OFF, |
| DEBUG |
| } |
| |
| |
| public static class ComponentSystemEventListenerWrapper implements SystemEventListener, StateHolder, ComponentSystemEventListener |
| { |
| ComponentSystemEventListenerWrapper(ComponentSystemEventListener listener, UIComponent component) |
| { |
| _delegate = listener; |
| _componentClass = component.getClass(); |
| } |
| |
| // Default constructor for state restoration |
| public ComponentSystemEventListenerWrapper() |
| { |
| } |
| |
| @Override |
| public boolean equals(Object o) |
| { |
| if (o == this) |
| { |
| return true; |
| } |
| else if (o instanceof ComponentSystemEventListenerWrapper) |
| { |
| ComponentSystemEventListenerWrapper other = (ComponentSystemEventListenerWrapper) o; |
| return _componentClass.equals(other._componentClass) && _delegate.equals(other._delegate); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return _componentClass.hashCode() + _delegate.hashCode(); |
| } |
| |
| // SystemEventListener implementation |
| |
| @Override |
| public void processEvent(SystemEvent event) throws AbortProcessingException |
| { |
| assert (event instanceof ComponentSystemEvent); |
| processEvent((ComponentSystemEvent) event); |
| } |
| |
| @Override |
| public void processEvent(ComponentSystemEvent event) |
| { |
| _delegate.processEvent((ComponentSystemEvent) event); |
| } |
| |
| @Override |
| public boolean isListenerForSource(Object source) |
| { |
| if (_delegate instanceof SystemEventListener) |
| { |
| return ((SystemEventListener)_delegate).isListenerForSource(source); |
| } |
| |
| // From the spec: and its implementation of SystemEventListener.isListenerForSource(java.lang.Object) must return true |
| // if the instance class of this UIComponent is assignable from the argument to isListenerForSource. |
| return _componentClass.isAssignableFrom(source.getClass()); |
| } |
| |
| // StateHolder Implementation |
| |
| @Override |
| public Object saveState(FacesContext context) |
| { |
| if (_delegate instanceof UIComponent) |
| { |
| return null; |
| } |
| |
| Object[] state = new Object[2]; |
| state[0] = StateUtils.saveStateHolder(context, _delegate); |
| state[1] = _componentClass; |
| |
| return state; |
| } |
| |
| @Override |
| public void restoreState(FacesContext context, Object state) |
| { |
| if (state == null) |
| { |
| return; |
| } |
| |
| Object[] stateArr = (Object[]) state; |
| Object saved = stateArr[0]; |
| |
| _delegate = (ComponentSystemEventListener) ((saved == null) ? UIComponent .getCurrentComponent(context) |
| : StateUtils.restoreStateHolder(context, saved)); |
| _componentClass = (Class<?>)stateArr[1]; |
| } |
| |
| @Override |
| public boolean isTransient() |
| { |
| if (_delegate instanceof StateHolder) |
| { |
| return ((StateHolder)_delegate).isTransient(); |
| } |
| return false; |
| } |
| |
| @Override |
| public void setTransient(boolean isTransient) |
| { |
| if (_delegate instanceof StateHolder) |
| { |
| ((StateHolder)_delegate).setTransient(isTransient); |
| } |
| } |
| |
| |
| private ComponentSystemEventListener _delegate; |
| private Class<?> _componentClass; |
| } |
| |
| private static final ClientIdCaching _CLIENT_ID_CACHING = _initClientIdCaching(); |
| |
| // System property controlling whether client ID caching is enabled |
| private static final String _SYSTEM_PROP_CLIENT_ID_CACHING = |
| "org.apache.myfaces.trinidad.CLIENT_ID_CACHING"; |
| |
| // Application map key indicating that we've already warned once |
| // that the client id caching configuration is not optimized for |
| // production mode. |
| private static final String _WARNED_CLIENT_ID_CACHING_KEY = |
| "org.apache.myfaces.trinidad.WARNED_CLIENT_ID_CACHING"; |
| |
| static private final LifecycleRenderer _UNDEFINED_LIFECYCLE_RENDERER = |
| new ExtendedRendererImpl(); |
| static private final Renderer _UNDEFINED_RENDERER = new RendererImpl(); |
| } |