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

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

There is a newer version: 2.2.1
Show 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.IOException;

import java.util.Collection;
import java.util.Iterator;

import javax.el.MethodExpression;

import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.ProjectStage;
import javax.faces.component.NamingContainer;
import javax.faces.component.StateHelper;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.component.UIPanel;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitHint;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import javax.faces.render.Renderer;

import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.context.ComponentContextChange;
import org.apache.myfaces.trinidad.context.ComponentContextManager;
import org.apache.myfaces.trinidad.context.PartialPageContext;
import org.apache.myfaces.trinidad.context.RenderingContext;
import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.event.AttributeChangeListener;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.render.CoreRenderer;


/**
 * Pure abstract base class for all UIX components.
 */
@JSFComponent
abstract public class UIXComponent extends UIComponent
{
  /**
   * Helper function called by Renderers to iterate over a flattened view of a group of
   * potentially FlattenedComponent instances rooted at a single child of the component to collect
   * information about these children prior to encoding the children using
   * encodeFlattenedChild(FacesContext, ComponentProcessor, UIComponent, Object).
   * 

* If the child is a FlattenedComponent, the childProcessor will * be called on each of that FlattenedComponent's children, recursing if those children are * themselves FlattenedComponents, otherwise, the childProcessor will be called on * the child itself. *

* If the Renderer accidentally passes in the component to be processed instead of one * of its children, the result will almost certainly be an infinite recursion and stack overflow. * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, UIComponent, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, Iterable, Object) * @see FlattenedComponent */ public static boolean processFlattenedChildren( FacesContext context, ComponentProcessor childProcessor, UIComponent child, S callbackContext) throws IOException { return processFlattenedChildren(context, new ComponentProcessingContext(), childProcessor, child, callbackContext); } /** * Helper function called by Renderers to encode a flattened view of a group of * potentially FlattenedComponent instances rooted at a single child of the component, * invoking the childProcessor with its * callbackContext on each renderable instance. This method must be called * when the childProcessor is actually encoding and the childProcessor must not attempt * to encode the same component instances more than once per request. *

* If a Renderer needs to * collect information about its possibly flattened children before calling * encodeFlattenedChild(FacesContext, ComponentProcessor, UIComponent, Object), * it should call processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object) * to collect the information. *

* If the child is a FlattenedComponent, the childProcessor will * be called on each of that FlattenedComponent's children, recursing if those children are * themselves FlattenedComponents, otherwise, the childProcessor will be called on * the child itself. *

* FlattenedComponents that wish to check whether they are processed for the purpose of * encoding can check the ProcessingHints of the ComponentProcessingContext for the * presence of PROCESS_FOR_ENCODING hint. *

* If the Renderer accidentally passes in the component to be encoded instead of one * of its children, the result will almost certainly be an infinite recursion and stack overflow. * @return true If any children were processed * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object) * @see FlattenedComponent */ public static boolean encodeFlattenedChild( FacesContext context, ComponentProcessor childProcessor, UIComponent child, S callbackContext) throws IOException { ComponentProcessingContext processingContext = new ComponentProcessingContext(); processingContext.__setIsRendering(); return processFlattenedChildren(context, processingContext, childProcessor, child, callbackContext); } /** * Helper function called by FlattenedComponent to iterate over a flattened view of a group of * potentially FlattenedComponent instances rooted at a single child of the FlattenedComponent, * invoking the childProcessor with its * callbackContext on each renderable instance. *

* If the child is a FlattenedComponent, the childProcessor will * be called on each of that FlattenedComponent's children, recursing if those children are * themselves FlattenedComponents, otherwise, the childProcessor will be called on * the child itself. *

* This method is typically used to flatten the contents of a facet of the FlattenedComponent. * If the FlattenedComponent accidentally passes in itself instead of one * of its children, the result will almost certainly be an infinite recursion and stack overflow. * @return true If any children were processed * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, Iterable, Object) * @see FlattenedComponent */ public static boolean processFlattenedChildren( FacesContext context, ComponentProcessingContext cpContext, ComponentProcessor childProcessor, UIComponent child, S callbackContext) throws IOException { if (child.isRendered()) { // component is an action FlattenedComponent. Ask it to flatten its children if ((child instanceof FlattenedComponent) && ((FlattenedComponent)child).isFlatteningChildren(context)) { return ((FlattenedComponent)child).processFlattenedChildren(context, cpContext, childProcessor, callbackContext); } else { boolean processed = true; RequestContext requestContext = cpContext.getRequestContext(); requestContext.pushCurrentComponent(context, child); child.pushComponentToEL(context, null); try { if (isFlattenableCoreComponent(child)) { processed = processFlattenedChildren(context, cpContext, childProcessor, child.getChildren(), callbackContext); } else { try { // not a FlattenedComponent, pass the component directly to the ComponentProcessor childProcessor.processComponent(context, cpContext, child, callbackContext); } finally { // if startDepth is > 0, only the first visible child will be marked as starting a group cpContext.resetStartDepth(); } } } finally { child.popComponentFromEL(context); requestContext.popCurrentComponent(context, child); } return processed; } } else { // component not rendered return false; } } /** * Helper function called by Renderers to iterate over a flattened view of the * children, potentially containing FlattenedComponents, of the component the Renderer is * encoding, invoking the childProcessor with its * callbackContext on each renderable instance. *

* For each FlattenedComponent child, the childProcessor will * be called on each of that FlattenedComponent's children, recursing if those children are * themselves FlattenedComponents, otherwise, the childProcessor will be called on * the child itself. *

* This method is typically used to flatten the children of the FlattenedComponent to * be encoded. * @return true If any children were processed * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, UIComponent, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, Iterable, Object) * @see FlattenedComponent */ public static boolean processFlattenedChildren( FacesContext context, ComponentProcessor childProcessor, Iterable children, S callbackContext) throws IOException { return processFlattenedChildren(context, new ComponentProcessingContext(), childProcessor, children, callbackContext); } /** * Helper function called by Renderers to encode a flattened view of their children, * invoking the childProcessor with its * callbackContext on each renderable instance. This method must be called * when the childProcessor is actually encoding and the childProcessor must not attempt * to encode the same component instances more than once per request. *

* If a Renderer needs to * collect information about its possibly flattened children before calling * encodeFlattenedChild(FacesContext, ComponentProcessor, Iterable<UIComponent>, Object), * it should call * processFlattenedChildren(FacesContext, ComponentProcessor, Iterable<UIComponent>, Object) * to collect the information. *

* For each FlattenedComponent child, the childProcessor will * be called on each of that FlattenedComponent's children, recursing if those children are * themselves FlattenedComponents, otherwise, the childProcessor will be called on * the child itself. *

* FlattenedComponents that wish to check whether they are processed for the purpose of * encoding can check the ProcessingHints of the ComponentProcessingContext for the * presence of PROCESS_FOR_ENCODING hint. * @param context FacesContext * @param childProcessor ComponentProcessor to call on each flattened child * @param children Initial set of children to flatten * @param callbackContext context object to pass to the childProcessor on each invocation * @return true If any children were processed * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object) * @see FlattenedComponent */ public static boolean encodeFlattenedChildren( FacesContext context, ComponentProcessor childProcessor, Iterable children, S callbackContext) throws IOException { ComponentProcessingContext processingContext = new ComponentProcessingContext(); processingContext.__setIsRendering(); return processFlattenedChildren(context, processingContext, childProcessor, children, callbackContext); } /** * Helper function called by FlattenedComponents to iterate over a flattened view of their * children, potentially themselves FlattenedComponents, invoking the childProcessor * with its callbackContext on each renderable instance. *

* For each FlattenedComponent child, the childProcessor will * be called on each of that FlattenedComponent's children, recursing if those children are * themselves FlattenedComponents, otherwise, the childProcessor will be called on * the child itself. * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object) * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, UIComponent, Object) * @see FlattenedComponent */ public static boolean processFlattenedChildren( FacesContext context, ComponentProcessingContext cpContext, ComponentProcessor childProcessor, Iterable children, S callbackContext) throws IOException { // we haven't processed a child yet boolean processedChild = false; for (UIComponent currChild : children) { // latch processed child to the first child processed processedChild |= processFlattenedChildren(context, cpContext, childProcessor, currChild, callbackContext); } return processedChild; } /** * Broadcast the FacesEvent after updating the current component and * current composite component * * @param context The current instance of FacesContext * @param event The FacesEvent to be broadcasted */ public static void broadcastInContext(FacesContext context, FacesEvent event) { UIComponent component = event.getComponent(); UIComponent compositeParent = null; RequestContext requestContext = RequestContext.getCurrentInstance(); if (!UIComponent.isCompositeComponent(component)) { compositeParent = UIComponent.getCompositeComponentParent(component); if (compositeParent != null) { requestContext.pushCurrentComponent(context, compositeParent); compositeParent.pushComponentToEL(context, null); } } requestContext.pushCurrentComponent(context, component); component.pushComponentToEL(context, null); try { component.broadcast(event); } finally { component.popComponentFromEL(context); requestContext.popCurrentComponent(context, component); if (compositeParent != null) { compositeParent.popComponentFromEL(context); requestContext.popCurrentComponent(context, compositeParent); } } } /** *

Perform a tree visit starting at * this node in the tree.

* *

UIXComponent.visitTree() implementations do not invoke the * {@code VisitCallback}directly, but instead call * {@code VisitContext.invokeVisitCallback()}to invoke the * callback. This allows {@code VisitContext}implementations * to provide optimized tree traversals, for example by only * calling the {@code VisitCallback}for a subset of components.

* * @param visitContext the VisitContext for this visit * @param callback the VisitCallback instance * whose visit method will be called * for each node visited. * @return component implementations may return true * to indicate that the tree visit is complete (eg. all components * that need to be visited have been visited). This results in * the tree visit being short-circuited such that no more components * are visited. * * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback() */ public boolean visitTree( VisitContext visitContext, VisitCallback callback) { return visitTree(visitContext, this, callback); } /** * Specifies what facets and children components should be processed as rendered for life-cycle * methods. Any components not returned will not be processed during methods such as decoding, * validating, updating the model, rendered-only tree visiting, etc. * * @param facesContext the facesContext * @return An iterator of components to process. Must not return null (return an empty iterator * if no children components should be processed). */ protected Iterator getRenderedFacetsAndChildren( FacesContext facesContext) { return defaultGetRenderedFacetsAndChildren(facesContext); } /** * Default implementation of getRenderedFacetsAndChildren for cases where a * UIXComponent subclass wants to restore the default implementation that one of its * superclasses have overridden. * * * @param facesContext the facesContext * @return An iterator of components to process. Must not return null (return an empty iterator * if no children components should be processed). */ protected final Iterator defaultGetRenderedFacetsAndChildren( FacesContext facesContext) { Renderer renderer = getRenderer(facesContext); if (renderer instanceof CoreRenderer) { return ((CoreRenderer)renderer).getRenderedFacetsAndChildren(facesContext, this); } else { return getFacetsAndChildren(); } } /** * Hook for subclasses to override the manner in which the component's children are visited. The default * implementation visits all of the children and facets of the Component. * setupChildrenVisitingContext will have been called before this method is * invoked and tearDownChildrenVisitingContext will be called after. * respectively. If the purpose of this visit was to encode the component and the * component uses a CoreRenderer, the CoreRenderer's * setupChildrenEncodingContext and tearDownChildrenEncodingContext * will be called before and after this method is invoked, respectively. * @param visitContext the VisitContext for this visit * @param callback the VisitCallback instance * @return true if the visit is complete. * @see #setupChildrenVisitingContext * @see #tearDownChildrenVisitingContext * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupChildrenEncodingContext * @see org.apache.myfaces.trinidad.render.CoreRenderer#tearDownChildrenEncodingContext */ protected boolean visitChildren( VisitContext visitContext, VisitCallback callback) { // See if this is during encoding, if so, allow the renderer to control the visitation of // the children so that any special encoding context may be applied around the visitation // of each child. if (_isEncodingVisit(visitContext)) { Renderer renderer = getRenderer(visitContext.getFacesContext()); if (renderer instanceof CoreRenderer) { CoreRenderer coreRenderer = (CoreRenderer)renderer; return coreRenderer.visitChildrenForEncoding(this, visitContext, callback); } } // visit all of the children of the component return visitAllChildren(visitContext, callback); } /** * Default implementation of visiting children that visits all children without iterating * @param visitContext the VisitContext for this visit * @param callback the VisitCallback instance * @return true if the visit is complete. */ protected final boolean visitAllChildren( VisitContext visitContext, VisitCallback callback) { // visit the children of the component Iterator kids = visitContext.getHints().contains(VisitHint.SKIP_UNRENDERED) ? getRenderedFacetsAndChildren(visitContext.getFacesContext()) : getFacetsAndChildren(); while (kids.hasNext()) { // If any kid visit returns true, we are done. if (kids.next().visitTree(visitContext, callback)) { return true; } } return false; } /** * Returns true if the components are being visited * for the purpose of encoding. */ private static boolean _isEncodingVisit(VisitContext visitContext) { return(visitContext.getHints().contains(VisitHint.EXECUTE_LIFECYCLE) && FacesContext.getCurrentInstance().getCurrentPhaseId() == PhaseId.RENDER_RESPONSE); } /** *

Perform a tree visit starting at the specified node in the tree.

* *

UIXComponent.visitTree() implementations do not invoke the * {@code VisitCallback}directly, but instead call * {@code VisitContext.invokeVisitCallback()}to invoke the * callback. This allows {@code VisitContext}implementations * to provide optimized tree traversals, for example by only * calling the {@code VisitCallback}for a subset of components.

* * @param visitContext the VisitContext for this visit * @param component the UIComponent to start the visit from * @param callback the VisitCallback instance * whose visit method will be called * for each node visited. * @return component implementations may return true * to indicate that the tree visit is complete (eg. all components * that need to be visited have been visited). This results in * the tree visit being short-circuited such that no more components * are visited. * * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback() */ public static boolean visitTree( VisitContext visitContext, UIComponent component, VisitCallback callback) { // push component on to the stack at the beginning of visiting tree. RequestContext requestContext = RequestContext.getCurrentInstance(); requestContext.pushCurrentComponent(visitContext.getFacesContext(), component); try { if (!(component instanceof UIXComponent)) { // hopefully the subview implementations have the subId optimization return component.visitTree(visitContext, callback); } else { UIXComponent uixComponent = (UIXComponent)component; FacesContext context = visitContext.getFacesContext(); // delegate to the UIXComponent if (!uixComponent.isVisitable(visitContext)) return false; // set up the EL Context with the component. Note that since we do this after call // isVisitable, any attributes retrieved (liek rendered) that are bound with EL referring // to the current component will be evaluated correctly, however, in the specific case // of rendered, rendered already has this problem in normal JSF traversal since it // is evaluated by the parent component component.pushComponentToEL(context, null); boolean doneVisiting = false; RuntimeException re = null; try { RenderingContext rc = (_isEncodingVisit(visitContext)) ? RenderingContext.getCurrentInstance() : null; // UIXComponents are allowed to set up their context differently for encoding // than normal processing, so behave differently if this is the RenderResponse // phase. In order to allow the visitcallback to call encodeAll in the ppr case, // we don't call setupEncodingContext before we call the visitContext, since this // would result in setupEncodingContext being called twice on the partial roots, // instead we only do so if the visitCallback returns ACCEPT if (rc == null) { uixComponent.setupVisitingContext(context); } VisitResult visitResult = VisitResult.REJECT; try { // invoke the callback for this component visitResult = visitContext.invokeVisitCallback(component, callback); if (visitResult == VisitResult.COMPLETE) doneVisiting = true; else if (visitResult == VisitResult.ACCEPT) { // now determine whether we need to visit the children // assume that all UIXComponent NamingContainers always act as NamingContainers, // (unlike ) and this it is OK to put the optimization where we // don't visit the children if we know that we don't have any ids in this // subtree to visit boolean skipChildren = (uixComponent instanceof NamingContainer) && visitContext.getSubtreeIdsToVisit(uixComponent).isEmpty(); // visit the children of the component if we aren't supposed to skip them if (!skipChildren) { // setup encoding context before visiting children, since we didn't do so // before calling the visitCallback if (rc != null) { uixComponent.setupEncodingContext(context, rc); } try { doneVisiting = visitChildren(visitContext, uixComponent, callback); } finally { // teardown the encoding context if we set it up if (rc != null) { uixComponent.tearDownEncodingContext(context, rc); } } } } else { // don't visit the children assert(visitResult == VisitResult.REJECT); } } catch (RuntimeException ex) { re = ex; } finally { try { // tear down the context we set up in order to visit our component if (rc == null) { uixComponent.tearDownVisitingContext(context); } } catch (RuntimeException ex) { if (re == null) { throw ex; } else { _LOG.warning(ex); } } } } finally { component.popComponentFromEL(context); if (re != null) { throw re; } } // if we got this far, we're not done return doneVisiting; } } finally { // pop component out after visiting tree. requestContext.popCurrentComponent(visitContext.getFacesContext(), component); } } /** * Utility method to allow the visiting of children components while visiting a parent using * a new visit callback or visit context. The method may only be called when the parent is * is the target of a visitation to ensure that it is properly in context. *

Example usage:

*
@Override
   * public VisitResult visit(
   *   VisitContext visitContext,
   *   UIComponent  target)
   * {
   *   if (someCondition)
   *   {
   *     UIXComponent.visitChildren(target, visitContext, new VisitCallback() {...});
   *     return VisitResult.COMPLETE;
   *   }
   *   ...
   * }
* * @param visitContext the VisitContext for this visit * @param parentComponent the UIComponent to visit the children. The parent component * must be actively being visited in order to call this method. * @param callback the VisitCallback instance * whose visit method will be called * for each node visited. * @return component implementations may return true * to indicate that the tree visit is complete (eg. all components * that need to be visited have been visited). This results in * the tree visit being short-circuited such that no more components * are visited. */ public static boolean visitChildren( VisitContext visitContext, UIComponent parentComponent, VisitCallback callback) { if (!(parentComponent instanceof UIXComponent)) { // Not a UIXComponent, there is no extra functionality necessary in order to visit the // children. for (Iterator iter = parentComponent.getFacetsAndChildren(); iter.hasNext();) { UIComponent child = iter.next(); if (child.visitTree(visitContext, callback)) { return true; } } return false; } UIXComponent uixParentComponent = (UIXComponent)parentComponent; FacesContext context = visitContext.getFacesContext(); RenderingContext rc = (_isEncodingVisit(visitContext)) ? RenderingContext.getCurrentInstance() : null; boolean doneVisiting = false; RuntimeException re = null; // setup any context needed for visiting the children of the component as opposed // to the component itself if (parentComponent instanceof UIXComponent) { if (rc != null) { uixParentComponent.setupChildrenEncodingContext(context, rc); } else { uixParentComponent.setupChildrenVisitingContext(context); } } try { doneVisiting = uixParentComponent.visitChildren(visitContext, callback); } catch (RuntimeException ex) { re = ex; } finally { try { // teardown any context initialized above if (rc != null) { uixParentComponent.tearDownChildrenEncodingContext(context, rc); } else { uixParentComponent.tearDownChildrenVisitingContext(context); } } catch (RuntimeException ex) { if (re == null) { throw ex; } else { _LOG.warning(ex); } } if (re != null) { throw re; } } return doneVisiting; } /** * Add a component as a partial target to the current request. This code handles the * delegation to {@link #setPartialTarget(FacesContext, PartialPageContext)} * for UIXComponents or assumes for {@link UIComponent} that components with a renderer * type are able to produce DOM elements that have IDs that can be replaced. * * @param facesContext the faces context * @param partialContext the partial page context * @param component the component to add as a target */ public static void addPartialTarget(FacesContext facesContext, PartialPageContext partialContext, UIComponent component) { if(component == null) { throw new NullPointerException("UIComponent is null"); } if (component instanceof UIXComponent) { ((UIXComponent)component).setPartialTarget(facesContext, partialContext); } else { // default to using the renderer type implementation _addPartialTargetImpl(facesContext, partialContext, component); } } /** * Clears all of the cached clientIds in this component subtree */ public void clearCachedClientIds() { clearCachedClientIds(this); } /** * Clears all of the cached clientIds in the component's subtree * @param component Component subtree to clear the cached client ids for */ public static void clearCachedClientIds(UIComponent component) { if (component instanceof UIXComponent) { ((UIXComponent)component).clearCachedClientIds(); } else { _clearCachedClientIds(component); } } /** * Default implementation of clearing the cached client ids */ private static void _clearCachedClientIds(UIComponent component) { // clear this component String id = component.getId(); component.setId(id); // clear the children Iterator allChildren = component.getFacetsAndChildren(); while (allChildren.hasNext()) { clearCachedClientIds(allChildren.next()); } } /** * Marks this component as a partial target for this request. Typically called * by the {@link org.apache.myfaces.trinidad.context.RequestContext RequestContext}. * The component should add the client ID the desired rendered component to the context. * This allows components that do not render a replacable DOM element with an ID * to choose an alternative component, like a parent. * * @param facesContext the faces context * @param partialContext the partial page context */ protected void setPartialTarget(FacesContext facesContext, PartialPageContext partialContext) { UIXComponent._addPartialTargetImpl(facesContext, partialContext, this); } /** *

Called by * {@link UIXComponent#visitTree(VisitContext,VisitCallback) UIXComponent.visitTree()}to determine * whether this component is "visitable" - ie. whether this component * satisfies the {@link VisitHint}returned by * {@link VisitContext#getHints VisitContext.getHints()}.

*

If this component is not visitable (ie. if this method returns * false), the tree visited is short-circuited such that neither the * component nor any of its descendents will be visited>

*

Custom {@code treeVisit()}implementations may call this method * to determine whether the component is visitable before performing * any visit-related processing.

* @param visitContext VisitingContext to use to determine if the component is visitable * @return true if this component should be visited, false otherwise. */ protected boolean isVisitable(VisitContext visitContext) { return _isVisitable(visitContext, this); } /** * @see #addPartialTarget(FacesContext, PartialPageContext, UIComponent) * @see #setPartialTarget(FacesContext, PartialPageContext) */ private static void _addPartialTargetImpl( FacesContext facesContext, PartialPageContext partialContext, UIComponent component) { if (component.getRendererType() == null) { if (component.getParent() != null) { // delegate to the parent component, assuming that no renderer type means that // there is no suitable replacable DOM element for this component addPartialTarget(facesContext, partialContext, component.getParent()); } } else { partialContext.addPartialTarget(component.getClientId(facesContext)); } } /** * default implementation checking the VisitHint.SKIP_TRANSIENT and * VisitHint.SKIP_UNRENDERED hints. */ private static boolean _isVisitable(VisitContext visitContext, UIComponent component) { Collection hints = visitContext.getHints(); if (hints.contains(VisitHint.SKIP_TRANSIENT) && component.isTransient()) return false; if (hints.contains(VisitHint.SKIP_UNRENDERED) && !component.isRendered()) return false; return true; } /** *

* Called when visiting the component during optimized partial page encoding so that the * component can modify what is actually encoded. For example tab controls often * render the tabs for the ShowDetailItems in the tab bar before delegating to the * disclosed ShowDetailItem to render the tab content. As a result, the tab control * needs to encode its tab bar if any of its ShowDetailItems are partial targets so that * the tab labels, for example, are up-to-date. *

*

* The default implementation delegates to the CoreRenderer if this component has one, otherwise * it calls the VisitCallback and returns its result if this UIXComponent is a partial * target of the current encoding. *

* @param visitContext VisitContext to pass to the VisitCallback * @param partialContext PartialPageContext for the current partial encoding * @param callback VisitCallback to call if this component is a partial target * @return The VisitResult controlling continued iteration of the visit. */ public VisitResult partialEncodeVisit( VisitContext visitContext, PartialPageContext partialContext, VisitCallback callback) { FacesContext context = visitContext.getFacesContext(); Renderer renderer = getRenderer(context); if (renderer instanceof CoreRenderer) { // delegate to the CoreRenderer return ((CoreRenderer)renderer).partialEncodeVisit(visitContext, partialContext, this, callback); } else { // check that this is a component instance that should be encoded if (partialContext.isPossiblePartialTarget(getId()) && partialContext.isPartialTarget(getClientId(context))) { // visit the component instance return callback.visit(visitContext, this); } else { // Not visiting this component, but allow visit to // continue into this subtree in case we've got // visit targets there. return VisitResult.ACCEPT; } } } /** *

Sets up the context necessary to visit or invoke the component for all phases.

*

The default implementation does nothing.

*

If a subclass overrides this method, it should override * tearDownVisitingContext as well.

*

It is guaranteed that if setupVisitingContext completes * tearDownVisitingContext will be called for this component

* @param context FacesContext * @see #tearDownVisitingContext * @see #setupEncodingContext * @see #tearDownEncodingContext * @see #setupChildrenVistingContext */ protected void setupVisitingContext(@SuppressWarnings("unused") FacesContext context) { // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // First, check to ensure that we are not already in context if (_inVisitingContext) { _handleInvalidContextUsage("COMPONENT_ALREADY_IN_VISITING_CONTEXT", _setupVisitingCaller); } // Next, add a context change so that the flags are reset during an // invokeOnComponent(context, clientId, callback), or a visitTree call: ComponentContextManager componentContextManager = RequestContext.getCurrentInstance().getComponentContextManager(); componentContextManager.pushChange(new VisitDebugContextChange(this)); _inVisitingContext = true; _setupVisitingCaller = _getStackTraceElementForCaller(); } } /** *

Sets up the context necessary to visit or invoke the children of a component for all phases. *

*

The default implementation does nothing.

*

If a subclass overrides this method, it should override * tearDownChildrenVisitingContext as well.

*

It is guaranteed that if setupChildrenVisitingContext completes * tearDownChildrenVisitingContext will be called for this component

* @param context FacesContext * @see #visitChildren * @see #tearDownChildrenVisitingContext * @see setupVisitingContext */ protected void setupChildrenVisitingContext(@SuppressWarnings("unused") FacesContext context) { // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // Check to ensure that we are not already in context if (_inChildrenVisitingContext) { _handleInvalidContextUsage("COMPONENT_ALREADY_IN_CHILDREN_VISITING_CONTEXT", _setupChildrenVisitingCaller); } // Next, add a context change so that the flags are reset during an // invokeOnComponent(context, clientId, callback), or a visitTree call // (Note that a separate one is needed for the children due to the fact that currently // the encoding context / visiting context is not set up during normal tree traversal, // but the children encoding context is setup by the renderers): ComponentContextManager componentContextManager = RequestContext.getCurrentInstance().getComponentContextManager(); componentContextManager.pushChange(new VisitChildrenDebugContextChange(this)); _inChildrenVisitingContext = true; _setupChildrenVisitingCaller = _getStackTraceElementForCaller(); } } /** *

Tears down context created in order to visit or invoke the component * for all phases.

*

The default implementation does nothing.

*

A subclass should only override this method if it overrode * setupVisitingContext as well

*

It is guaranteed that tearDownVisitingContext will be called only after * setupVisitingContext has been called for this component

* @param context FacesContext * @see #setupVisitingContext * @see #setupEncodingContext * @see #tearDownEncodingContext */ protected void tearDownVisitingContext(@SuppressWarnings("unused") FacesContext context) { // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // First, check to ensure that we are not already in context if (!_inVisitingContext) { _handleInvalidContextUsage("COMPONENT_NOT_IN_VISITING_CONTEXT", _tearDownVisitingCaller); } // Next, remove the context change that was added in setupVisitingContext: ComponentContextManager componentContextManager = RequestContext.getCurrentInstance().getComponentContextManager(); ComponentContextChange contextChange = componentContextManager.popChange(); // Validate the state of the context change stack: if (!(contextChange instanceof VisitDebugContextChange) || ((VisitDebugContextChange)contextChange)._component != this) { throw new IllegalStateException(_getInvalidContextChangeMessage( VisitDebugContextChange.class, contextChange)); } _inVisitingContext = false; _tearDownVisitingCaller = _getStackTraceElementForCaller(); } } /** *

Tears down context created in order to visit or invoke the children of a component * for all phases.

*

The default implementation does nothing.

*

A subclass should only override this method if it overrode * setupChildrenVisitingContext as well

*

It is guaranteed that tearDownChildrenVisitingContext will be called only after * setupChildrenVisitingContext has been called for this component

* @param context FacesContext * @see #setupChildrenVisitingContext * @see #visitChildren */ protected void tearDownChildrenVisitingContext(@SuppressWarnings("unused") FacesContext context) { // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // Check to ensure that we are not already in context if (!_inChildrenVisitingContext) { _handleInvalidContextUsage("COMPONENT_NOT_IN_CHILDREN_VISITING_CONTEXT", _tearDownChildrenVisitingCaller); } // Next, remove the context change that was added in setupChildrenVisitingContext: ComponentContextManager componentContextManager = RequestContext.getCurrentInstance().getComponentContextManager(); ComponentContextChange contextChange = componentContextManager.popChange(); // Validate the state of the context change stack: if (!(contextChange instanceof VisitChildrenDebugContextChange) || ((VisitChildrenDebugContextChange)contextChange)._component != this) { throw new IllegalStateException(_getInvalidContextChangeMessage( VisitChildrenDebugContextChange.class, contextChange)); } _inChildrenVisitingContext = false; _tearDownChildrenVisitingCaller = _getStackTraceElementForCaller(); } } @Deprecated protected final void setUpEncodingContext(FacesContext context, RenderingContext rc) { setupEncodingContext(context, rc); } /** *

Sets up the context necessary to encode the component.

*

The default implementation delegates to * CoreRenderer.setupEncodingContext and then calls * setupVisitingContext

*

If a subclass overrides this method, it should override * tearDownEncodingContext as well.

*

It is guaranteed that if setUpEncodingContext completes * tearDownEncodingContext will be called for this component

*

* During partial page rendering traversals, setupEncodingContext is not called * before the VisitCallback is invoked. This behavior is different than for * setupVisitingContext, which is always called before the VisitCallback * is invoked for non-partial page rendering visits. This difference in behavior allows the * VisitCallback in a partial page rendering visit to safely call * UIComponent.encodeAll, which in the case of a UIXComponent, will call * UIXComponent.setupEncodeContext. *

* @param context The FacesContext * @param rc RenderingContext to use for encoding * @see #setupVisitingContext * @see #setupChildrenEncodingContext * @see #tearDownVisitingContext * @see #tearDownEncodingContext * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupEncodingContext(FacesContext, RenderingContext, UIComponent) */ protected void setupEncodingContext(FacesContext context, RenderingContext rc) { setupVisitingContext(context); // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // Check to ensure that we are not already in context if (_inEncodingContext) { _handleInvalidContextUsage("COMPONENT_ALREADY_IN_ENCODING_CONTEXT", _setupEncodingCaller); } _inEncodingContext = true; _setupEncodingCaller = _getStackTraceElementForCaller(); } Renderer renderer = getRenderer(context); if (renderer instanceof CoreRenderer) { CoreRenderer coreRenderer = (CoreRenderer)renderer; coreRenderer.setupEncodingContext(context, rc, (UIComponent)this); } _inEncodingContext = true; } /** * Sets the context necessary to encode the children of a component. * @param context The FacesContext * @param rc RenderingContext to use for encoding * @see #setupChildrenVisitingContext * @see #tearDownChildrenEncodingContext * @see #setupEncodingContext * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupChildrenEncodingContext */ public void setupChildrenEncodingContext(FacesContext context, RenderingContext rc) { setupChildrenVisitingContext(context); // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // Check to ensure that we are not already in context if (_inChildrenEncodingContext) { _handleInvalidContextUsage("COMPONENT_ALREADY_IN_CHILDREN_ENCODING_CONTEXT", _setupChildrenEncodingCaller); } _inChildrenEncodingContext = true; _setupChildrenEncodingCaller = _getStackTraceElementForCaller(); } Renderer renderer = getRenderer(context); if (renderer instanceof CoreRenderer) { CoreRenderer coreRenderer = (CoreRenderer)renderer; coreRenderer.setupChildrenEncodingContext(context, rc, this); } _inChildrenEncodingContext = true; } /** *

Tears down the context created in order to encode the component

*

The default implementation delegates to * CoreRenderer.tearDownEncodingContext and then calls * tearDownVisitingContext

*

A subclass should only override this method if it overrode * setupEncodingContext as well

*

It is guaranteed that tearDownEncodingContext will be called only after * setupEncodingContext has been called for this component

* @param context The FacesContext * @param rc RenderingContext to use for encoding * @see #setupEncodingContext * @see #tearDownVisitingContext * @see #setupEncodingContext * @see #tearDownChildrenEncodingContext * @see org.apache.myfaces.trinidad.render.CoreRenderer#tearDownEncodingContext(FacesContext, RenderingContext, UIComponent) */ protected void tearDownEncodingContext( FacesContext context, RenderingContext rc) { // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // Check to ensure that we are not already in context if (!_inEncodingContext) { _handleInvalidContextUsage("COMPONENT_NOT_IN_ENCODING_CONTEXT", _tearDownEncodingCaller); } _inEncodingContext = false; _tearDownEncodingCaller = _getStackTraceElementForCaller(); } Renderer renderer = getRenderer(context); try { if (renderer instanceof CoreRenderer) { CoreRenderer coreRenderer = (CoreRenderer)renderer; coreRenderer.tearDownEncodingContext(context, rc, (UIComponent)this); } } finally { tearDownVisitingContext(context); } } /** * Tears down the context necessary to encode the children of a component. * @param context The FacesContext * @param rc RenderingContext to use for encoding * @see #setupChildrenVisitingContext * @see #tearDownChildrenEncodingContext * @see #tearDownEncodingContext * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupChildrenEncodingContext */ public void tearDownChildrenEncodingContext( FacesContext context, RenderingContext rc) { // If in testing phase, add code to determine if this function is being used correctly. if (_inTestingPhase) { // Check to ensure that we are not already in context if (!_inChildrenEncodingContext) { _handleInvalidContextUsage("COMPONENT_NOT_IN_CHILDREN_ENCODING_CONTEXT", _tearDownChildrenEncodingCaller); } _inChildrenEncodingContext = false; _tearDownChildrenEncodingCaller = _getStackTraceElementForCaller(); } Renderer renderer = getRenderer(context); try { if (renderer instanceof CoreRenderer) { CoreRenderer coreRenderer = (CoreRenderer)renderer; coreRenderer.tearDownChildrenEncodingContext(context, rc, this); } } finally { tearDownChildrenVisitingContext(context); _inChildrenEncodingContext = false; } } /** * Returns the FacesBean used for storing the component's state. */ abstract public FacesBean getFacesBean(); /** * Adds an AttributeChangeListener. Attribute change events are not * delivered for any programmatic change to a property. They are only * delivered when a renderer changes a property without the application's * specific request. An example of an attribute change events might * include the width of a column that supported client-side resizing. */ abstract public void addAttributeChangeListener(AttributeChangeListener acl); /** * Removes an AttributeChangeListener. Attribute change events are not * delivered for any programmatic change to a property. They are only * delivered when a renderer changes a property without the application's * specific request. An example of an attribute change events might * include the width of a column that supported client-side resizing. */ abstract public void removeAttributeChangeListener(AttributeChangeListener acl); /** * Gets the registered AttributeChangeListeners. */ abstract public AttributeChangeListener[] getAttributeChangeListeners(); /** * Sets a method binding to an AttributeChangeListener. Attribute * change events are not * delivered for any programmatic change to a property. They are only * delivered when a renderer changes a property without the application's * specific request. An example of an attribute change events might * include the width of a column that supported client-side resizing. */ abstract public void setAttributeChangeListener(MethodExpression me); /** * Gets the method binding to an AttributeChangeListener. Attribute * change events are not * delivered for any programmatic change to a property. They are only * delivered when a renderer changes a property without the application's * specific request. An example of an attribute change events might * include the width of a column that supported client-side resizing. */ abstract public MethodExpression getAttributeChangeListener(); abstract public void markInitialState(); /** * Provides additional context (the target child component for which the container * client ID is requested) to a naming container for constructing a client ID. * This is useful for components such as @link UIXTable and @link UIXTreeTable which need * to return different container client IDs for stamped and non-stamped child components. * @see UIXComponentBase#getClientId(FacesContext context) */ abstract public String getContainerClientId(FacesContext context, UIComponent child); /** * Provides a logical parent for this component (a parent in the context of the document where this component was * defined). The default implementation will simply call getParent(). Components that get relocated during * tag execution will return their original parent * @return logical parent component */ public UIComponent getLogicalParent() { return getParent(); } /** * Provides a logical parent for the component (a parent in the context of the document where the component was * defined). The default implementation will simply call getParent() on the component. Components that get relocated during * tag execution should have their original parent returned (if available). * @param component - child component whose parent is being retrieved * @return logical parent component */ public static UIComponent getLogicalParent(UIComponent component) { if (component instanceof UIXComponent) { return ((UIXComponent)component).getLogicalParent(); } return component.getParent(); } /** * We are using FacesBean to save state, which does not implement StateHelper, so * calling this method will call UnsupportedOperationException */ @Override protected StateHelper getStateHelper() { throw new UnsupportedOperationException(); } /** * We are using FacesBean to save state, which does not implement StateHelper, so * calling this method will call UnsupportedOperationException */ @Override protected StateHelper getStateHelper(boolean create) { throw new UnsupportedOperationException(); } private String _getStackTraceElementForCaller() { StackTraceElement[] elements = Thread.currentThread().getStackTrace(); // 0 is the call to getStackTrace, 1 is this method, 2 is the calling method in UIXComponent // so 3 (4th element) is the caller to the UIXComponent method, the one that is desired. String lineSep = System.getProperty("line.separator"); StringBuilder stack = new StringBuilder(); for (int i = 3, size = elements.length; i < size && i < 10; ++i) { stack.append(elements[i].toString()); stack.append(lineSep); } return stack.toString(); } /** * Determine if we can flatten a core JSF component. * @param component The component * @return true if the component is a core JSF component and we can * flatten it successfully. */ private static boolean isFlattenableCoreComponent(UIComponent component) { // Optimize the cases of UINamingContainer () and UIPanel - // we will treat these components as FlattenedComponents because they do not render // any DOM. // Also note that as of JSF 2.0, UINamingContainer components are built // by f:subview, as well as composite components. Class componentClass = component.getClass(); if (UINamingContainer.class == componentClass) { // Check to see if this component was created as a composite // component, which we cannot flatten return component.getFacet(UIComponent.COMPOSITE_FACET_NAME) == null; } // Note that JSF 2.0 creates UIPanel wrappers around multiple components // inside of return UIPanel.class == componentClass; } private void _handleInvalidContextUsage( String bundleKey, String originalStackTrace ) throws IllegalStateException { String errorMessage = _LOG.getMessage(bundleKey, new Object[] { getClientId(), originalStackTrace }); if (_treatContextualIssuesAsErrors) { throw new IllegalStateException(errorMessage); } else { _LOG.warning(errorMessage); } } private String _getInvalidContextChangeMessage( Class expectedClass, ComponentContextChange foundChange) { String type = expectedClass.getName(); String id = (getParent() == null) ? getId() : getClientId(); return _LOG.getMessage("INVALID_CONTEXT_CHANGE_FOUND", new Object[] { type, id, foundChange }); } private static class VisitDebugContextChange extends ComponentContextChange { private VisitDebugContextChange( UIXComponent component) { _component = component; } @Override public void resume(FacesContext facesContext) { _component._inVisitingContext = _inVisitingContext; _component._inEncodingContext = _inEncodingContext; _component._setupVisitingCaller = _setupVisitingCaller; _component._tearDownVisitingCaller = _tearDownVisitingCaller; _component._setupEncodingCaller = _setupEncodingCaller; _component._tearDownEncodingCaller = _tearDownEncodingCaller; } @Override public void suspend(FacesContext facesContext) { _inVisitingContext = _component._inVisitingContext; _inEncodingContext = _component._inEncodingContext; _setupVisitingCaller = _component._setupVisitingCaller; _tearDownVisitingCaller = _component._tearDownVisitingCaller; _setupEncodingCaller = _component._setupEncodingCaller; _tearDownEncodingCaller = _component._tearDownEncodingCaller; _component._inVisitingContext = false; _component._inEncodingContext = false; _component._setupVisitingCaller = null; _component._tearDownVisitingCaller = null; _component._setupEncodingCaller = null; _component._tearDownEncodingCaller = null; } @Override public String toString() { return String.format("VisitDebugContextChange(component: %s, id: %s)", _component, _component == null ? null : _component.getParent() == null ? _component.getId() : _component.getClientId()); } private final UIXComponent _component; private boolean _inVisitingContext; private boolean _inEncodingContext; private String _setupVisitingCaller; private String _tearDownVisitingCaller; private String _setupEncodingCaller; private String _tearDownEncodingCaller; } private static class VisitChildrenDebugContextChange extends ComponentContextChange { private VisitChildrenDebugContextChange( UIXComponent component) { _component = component; } @Override public void resume(FacesContext facesContext) { _component._inChildrenVisitingContext = _inChildrenVisitingContext; _component._inChildrenEncodingContext = _inChildrenEncodingContext; _component._setupChildrenEncodingCaller = _setupChildrenEncodingCaller; _component._tearDownChildrenEncodingCaller = _tearDownChildrenEncodingCaller; _component._setupChildrenVisitingCaller = _setupChildrenVisitingCaller; _component._tearDownChildrenVisitingCaller = _tearDownChildrenVisitingCaller; } @Override public void suspend(FacesContext facesContext) { _inChildrenVisitingContext = _component._inChildrenVisitingContext; _inChildrenEncodingContext = _component._inChildrenEncodingContext; _setupChildrenEncodingCaller = _component._setupChildrenEncodingCaller; _tearDownChildrenEncodingCaller = _component._tearDownChildrenEncodingCaller; _setupChildrenVisitingCaller = _component._setupChildrenVisitingCaller; _tearDownChildrenVisitingCaller = _component._tearDownChildrenVisitingCaller; _component._inChildrenVisitingContext = false; _component._inChildrenEncodingContext = false; _component._setupChildrenEncodingCaller = null; _component._tearDownChildrenEncodingCaller = null; _component._setupChildrenVisitingCaller = null; _component._tearDownChildrenVisitingCaller = null; } @Override public String toString() { return String.format("VisitChildrenDebugContextChange(component: %s, id: %s)", _component, _component == null ? null : _component.getParent() == null ? _component.getId() : _component.getClientId()); } private final UIXComponent _component; private boolean _inChildrenVisitingContext; private boolean _inChildrenEncodingContext; private String _setupChildrenEncodingCaller; private String _tearDownChildrenEncodingCaller; private String _setupChildrenVisitingCaller; private String _tearDownChildrenVisitingCaller; } // Use logging for now until all issues are resolved private final static boolean _treatContextualIssuesAsErrors; private final static boolean _inTestingPhase; private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponent.class); static { // If Trinidad is to be ever shared with a shared class loader, we should change this // static boolean flag to be non-class loader bound. Application app = null; try { FacesContext facesContext = FacesContext.getCurrentInstance(); app = facesContext == null ? null : facesContext.getApplication(); if (app == null) { ApplicationFactory appFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY); app = appFactory.getApplication(); } } catch (Throwable t) { // This occurs during unit testing without a full JSF environment, ignore ; } _inTestingPhase = app == null ? false : app.getProjectStage() == ProjectStage.UnitTest; if (_inTestingPhase) { _LOG.info("Application is running in testing phase, UIXComponent will " + "perform extra validation steps to ensure proper component usage"); } _treatContextualIssuesAsErrors = "true".equals( System.getProperty("uixcomponent.contextual.issue.throw")); }; private String _setupVisitingCaller; private String _tearDownVisitingCaller; private String _setupEncodingCaller; private String _tearDownEncodingCaller; private String _setupChildrenEncodingCaller; private String _tearDownChildrenEncodingCaller; private String _setupChildrenVisitingCaller; private String _tearDownChildrenVisitingCaller; private boolean _inVisitingContext; private boolean _inChildrenVisitingContext; private boolean _inEncodingContext; private boolean _inChildrenEncodingContext; }