All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.trinidad.component.UIXComponentBase Maven / Gradle / Ivy

The newest version!
/*
 * 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:
 * 
    *
  • Use of FacesBean for better and easier state saving
  • *
  • Support of the LifecycleRenderer class for greater Renderer * control over the lifecycle
  • *
  • 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).
  • *
*

FacesBean and UIXComponentBase

*

* UIXComponentBase differs from UIXComponent most particularly * in its use of FacesBeans to store all state. This offers * a number of advantages: *

    *
  • Subclassers - if they use FacesBean for their state as well - * do not need to write overrides of saveState() and restoreState(). *
  • *
  • State is optimized by default
  • *
  • Future optimizations - partly exposed today with * markInitialState() - can offer major state saving improvements. *
*

*/ // 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 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 null, 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 * null 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 addComponentChange(ComponentChange) 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 filterIter = (Iterator)getFacesBean().entries(_COMPONENT_CHANGE_FILTERS_KEY); ArrayList filterList = CollectionUtils.arrayList(filterIter); return filterList.toArray(new ComponentChangeFilter[filterList.size()]); } @Override protected Iterator 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 _getAncestors() { List ancestors = new ArrayList(); 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 ancestors = _getAncestors(); if (ancestors.isEmpty()) { return ""; } else { Iterator 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: *
    *
  • They must start with a letter (as defined by the Character.isLetter() method) or underscore ( _ ).
  • *
  • 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). *
  • *
*/ @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 allChildren = getFacetsAndChildren(); while (allChildren.hasNext()) { clearCachedClientIds(allChildren.next()); } } @Override abstract public String getFamily(); @Override public UIComponent getParent() { return _parent; } /** *

Set the parent UIComponent of this * UIComponent.

* * @param parent The new parent, or null 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)); } } /** *

Create (if necessary) and return a List of the children associated * with this component.

*/ @Override public List getChildren() { if (_children == null) _children = new ChildArrayList(this); return _children; } @Override public int getChildCount() { if (_children == null) return 0; else return getChildren().size(); } /** *

Create (if necessary) and return a Map of the facets associated * with this component.

*/ @Override public Map 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 getFacetNames() { if (_facets == null) return _EMPTY_STRING_ITERATOR; else return _facets.keySet().iterator(); } @Override public Iterator 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(_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 iter = (Iterator)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 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(); RequestContext requestContext = RequestContext.getCurrentInstance(); requestContext.pushCurrentComponent(context, this); pushComponentToEL(context, this); try { if (!isRendered()) return; // 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(); RequestContext requestContext = RequestContext.getCurrentInstance(); requestContext.pushCurrentComponent(context, this); pushComponentToEL(context, this); try { if (!isRendered()) return; // 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(); RequestContext requestContext = RequestContext.getCurrentInstance(); requestContext.pushCurrentComponent(context, this); pushComponentToEL(context, this); try { if (!isRendered()) return; // 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() + "]"; } /** *

Return the {@link FacesContext} instance for the current request.

*/ @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 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 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 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 iter = (Iterator)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 * null 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 * addComponentChange(new AttributeComponentChange(attributeName, attributeValue)); * This function is not final for backwards compatibility reasons, however, * existing subclassers whould override addComponentChange 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 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 invokeOnComponent on all of the * children of a component, surrounding the invocation with calls to * setup/tearDownChildrenVisitingContext. * This is useful when a component sometimes optimizes * away calling invokeOnComponent 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 children = getFacetsAndChildren(); while (children.hasNext() && !found) { found = children.next().invokeOnComponent(context, clientId, callback); } } finally { tearDownChildrenVisitingContext(context); } return found; } /** *

* Optimized implementation of invokeOnComponent for NamingContainers. * If the clientId isn't within the NamingContainer, invocation of the * NamingContainer's children is skipped. *

*

Subclasses implementing NamingContainer should override * invokeOnComponent and delegate to this method.

*/ 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 eventClass, ComponentSystemEventListener componentListener) { if (eventClass == null) { throw new NullPointerException("eventClass required"); } if (componentListener == null) { throw new NullPointerException("componentListener required"); } FacesBean bean = getFacesBean(); AttachedObjects, SystemEventListener> eventStorage = (AttachedObjects, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY); if (eventStorage == null) { eventStorage = new AttachedObjects, 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 eventClass, ComponentSystemEventListener componentListener) { if (eventClass == null) { throw new NullPointerException("eventClass required"); } if (componentListener == null) { throw new NullPointerException("componentListener required"); } FacesBean bean = getFacesBean(); AttachedObjects, SystemEventListener> eventStorage = (AttachedObjects, 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 getListenersForEventClass(Class eventClass) { FacesBean bean = getFacesBean(); AttachedObjects, SystemEventListener> eventStorage = (AttachedObjects, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY); if (eventStorage == null) { return Collections.emptyList(); } return eventStorage.getAttachedObjectList(eventClass); } public Map getPassThroughAttributes(boolean create) { if (_passthroughAttributesMap == null && create) { _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. *

This method must only * be called by classes that implement the interface, doing otherwise will result in an exception. *

* @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 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 behaviors = ( AttachedObjects)bean.getProperty(_CLIENT_BEHAVIORS_KEY); if (behaviors == null) { behaviors = new AttachedObjects(); 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 getEventNames() {} /** * Utility method to assist sub-classes in the implementation of the * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface. *

This method must only * be called by classes that implement the interface, doing otherwise will result in an exception. *

* @see javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors() * @return Read-only map of the client behaviors for this component */ protected Map> getClientBehaviors() { AttachedObjects behaviors = ( AttachedObjects)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. *

This method must only * be called by classes that implement the interface, doing otherwise will result in an exception. *

* @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 ------------------------- /** *

* This gets a single threadlocal shared stringbuilder instance, each time you call * __getSharedStringBuilder it sets the length of the stringBuilder instance to 0. *

* This allows you to use the same StringBuilder instance over and over. * You must call toString on the instance before calling __getSharedStringBuilder again. *

* Example that works *

   * StringBuilder sb1 = __getSharedStringBuilder();
   * sb1.append(a).append(b);
   * String c = sb1.toString();
   *
   * StringBuilder sb2 = __getSharedStringBuilder();
   * sb2.append(b).append(a);
   * String d = sb2.toString();
   * 
*

* Example that doesn't work, you must call toString on sb1 before * calling __getSharedStringBuilder again. *

   * StringBuilder sb1 = __getSharedStringBuilder();
   * StringBuilder sb2 = __getSharedStringBuilder();
   *
   * sb1.append(a).append(b);
   * String c = sb1.toString();
   *
   * sb2.append(b).append(a);
   * String d = sb2.toString();
   * 
* */ 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 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; } /** *

Verify that the specified component id is safe to add to the tree. *

* * @param id The proposed component id to check for validity * * @exception IllegalArgumentException if id * is null 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 _children; private Map _attributes; private Map _facets; private UIComponent _parent; private String _id; private String _clientId; private boolean _usesFacesBeanImpl; private Map _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 _EMPTY_STRING_ITERATOR = CollectionUtils.emptyIterator(); private static final Iterator _EMPTY_UICOMPONENT_ITERATOR = CollectionUtils.emptyIterator(); static private final ThreadLocal _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 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 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(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy