Skip to content

Instantly share code, notes, and snippets.

@bleathem
Created September 18, 2012 20:28
Show Gist options
  • Save bleathem/3745648 to your computer and use it in GitHub Desktop.
Save bleathem/3745648 to your computer and use it in GitHub Desktop.
AbstractTogglePanel - naive visitor impl
/*
* JBoss, Home of Professional Open Source
* Copyright ${year}, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.component;
import java.io.IOException;
import java.util.Iterator;
import javax.el.ELException;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UpdateModelException;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PostValidateEvent;
import javax.faces.event.PreValidateEvent;
import org.ajax4jsf.model.DataVisitResult;
import org.ajax4jsf.model.DataVisitor;
import org.richfaces.application.FacesMessages;
import org.richfaces.application.MessageFactory;
import org.richfaces.application.ServiceTracker;
import org.richfaces.cdk.annotations.Attribute;
import org.richfaces.cdk.annotations.EventName;
import org.richfaces.cdk.annotations.JsfComponent;
import org.richfaces.cdk.annotations.JsfRenderer;
import org.richfaces.cdk.annotations.Tag;
import org.richfaces.cdk.annotations.TagType;
import org.richfaces.component.util.MessageUtil;
import org.richfaces.context.ExtendedVisitContext;
import org.richfaces.context.ExtendedVisitContextMode;
import org.richfaces.event.ItemChangeEvent;
import org.richfaces.event.ItemChangeListener;
import org.richfaces.event.ItemChangeSource;
import org.richfaces.log.Logger;
import org.richfaces.log.RichfacesLogger;
import org.richfaces.renderkit.MetaComponentRenderer;
import org.richfaces.renderkit.html.TabRenderer;
import org.richfaces.renderkit.util.RendererUtils;
import com.google.common.base.Strings;
/**
* <p>The &lt;rich:togglePanel&gt; component is used as a base for the other switchable components, the
* &lt;rich:accordion&gt; component and the &lt;rich:tabPanel&gt; component. It provides an abstract switchable
* component without any associated markup. As such, the &lt;rich:togglePanel&gt; component could be customized to
* provide a switchable component when neither an accordion component or a tab panel component is appropriate.</p>
*
* @author akolonitsky
*/
@JsfComponent(tag = @Tag(type = TagType.Facelets, handler = "org.richfaces.view.facelets.html.TogglePanelTagHandler"), renderer = @JsfRenderer(type = "org.richfaces.TogglePanelRenderer"), attributes = {
"core-props.xml", "events-mouse-props.xml", "i18n-props.xml" })
public abstract class AbstractTogglePanel extends UIOutput implements AbstractDivPanel, ItemChangeSource,
MetaComponentResolver, MetaComponentEncoder {
public static final String ACTIVE_ITEM_META_COMPONENT = "activeItem";
public static final String COMPONENT_TYPE = "org.richfaces.TogglePanel";
public static final String COMPONENT_FAMILY = "org.richfaces.TogglePanel";
public static final String META_NAME_FIRST = "@first";
public static final String META_NAME_PREV = "@prev";
public static final String META_NAME_NEXT = "@next";
public static final String META_NAME_LAST = "@last";
// TODO What is MessageId ?
public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE";
private static final Logger LOG = RichfacesLogger.RENDERKIT.getLogger();
private static final RendererUtils UTILS = RendererUtils.getInstance();
private String submittedActiveItem = null;
private enum PropertyKeys {
localValueSet, required, valid, immediate, switchType
}
protected AbstractTogglePanel() {
setRendererType("org.richfaces.TogglePanelRenderer");
}
// -------------------------------------------------- Editable Value Holder
public Object getSubmittedValue() {
return this.submittedActiveItem;
}
public void resetValue() {
this.setValue(null);
this.setSubmittedValue(null);
this.setLocalValueSet(false);
this.setValid(true);
}
public void setSubmittedValue(Object submittedValue) {
this.submittedActiveItem = String.valueOf(submittedValue);
}
public boolean isLocalValueSet() {
return (Boolean) getStateHelper().eval(PropertyKeys.localValueSet, false);
}
public void setLocalValueSet(boolean localValueSet) {
getStateHelper().put(PropertyKeys.localValueSet, localValueSet);
}
public boolean isValid() {
return (Boolean) getStateHelper().eval(PropertyKeys.valid, true);
}
public void setValid(boolean valid) {
getStateHelper().put(PropertyKeys.valid, valid);
}
public boolean isRequired() {
return (Boolean) getStateHelper().eval(PropertyKeys.required, false);
}
/**
* <p>
* Set the "required field" state for this component.
* </p>
*
* @param required The new "required field" state
*/
public void setRequired(boolean required) {
getStateHelper().put(PropertyKeys.required, required);
}
/**
* Flag indicating that this component's value must be converted and validated immediately (that is, during Apply Request
* Values phase), rather than waiting until Process Validations phase.
*/
@Attribute
public boolean isImmediate() {
return (Boolean) getStateHelper().eval(PropertyKeys.immediate, false);
}
public void setImmediate(boolean immediate) {
getStateHelper().put(PropertyKeys.immediate, immediate);
}
// ----------------------------------------------------- UIComponent Methods
@Override
public void encodeBegin(FacesContext context) throws IOException {
updateActiveName(getActiveItem());
super.encodeBegin(context);
}
public String updateActiveName(String activeItemName) {
int itemIndex = -1;
if (!Strings.isNullOrEmpty(activeItemName)) {
itemIndex = getIndexByName(activeItemName);
}
if (itemIndex < 0) {
String firstItemName = getNameByIndex(0);
if (firstItemName != null) {
setActiveItem(firstItemName);
return firstItemName;
}
}
return activeItemName;
}
/**
* <p>
* Specialized decode behavior on top of that provided by the superclass. In addition to the standard
* <code>processDecodes</code> behavior inherited from {@link javax.faces.component.UIComponentBase}, calls
* <code>processValue()</code> if the the <code>immediate</code> property is true; if the component is invalid afterwards or
* a <code>RuntimeException</code> is thrown, calls {@link FacesContext#renderResponse}.
* </p>
*
* @throws NullPointerException {@inheritDoc}
*/
@Override
public void processDecodes(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, null);
final String activeItem = getActiveItemValue();
this.visit(this, new TogglePanelCallback() {
@Override
public DataVisitResult callback(FacesContext context, TogglePanelCoordinates coords) {
AbstractTogglePanelItemInterface item = coords.getItem();
if (item instanceof AbstractTab && (isActiveItem(item, activeItem) || getSwitchType() == SwitchType.client)) {
((UIComponent) item).processDecodes(context);
}
return DataVisitResult.CONTINUE;
}
});
// Process this component itself
try {
decode(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
} finally {
popComponentFromEL(context);
}
ItemChangeEvent event = createItemChangeEvent(context);
if (event != null) {
event.queue();
}
}
/**
* <p>
* In addition to the standard <code>processValidators</code> behavior inherited from
* {@link javax.faces.component.UIComponentBase}, calls <code>processValue()</code> if the <code>immediate</code> property
* is false (which is the default); if the component is invalid afterwards, calls {@link FacesContext#renderResponse}. If a
* <code>RuntimeException</code> is thrown during validation processing, calls {@link FacesContext#renderResponse} and
* re-throw the exception.
* </p>
*
* @throws NullPointerException {@inheritDoc}
*/
@Override
public void processValidators(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, null);
Application app = context.getApplication();
app.publishEvent(context, PreValidateEvent.class, this);
final String activeItem = getActiveItemValue();
this.visit(this, new TogglePanelCallback() {
@Override
public DataVisitResult callback(FacesContext context, TogglePanelCoordinates coords) {
AbstractTogglePanelItemInterface item = coords.getItem();
if (item instanceof AbstractTab && (isActiveItem(item, activeItem) || getSwitchType() == SwitchType.client)) {
((UIComponent) item).processValidators(context);
}
return DataVisitResult.CONTINUE;
}
});
app.publishEvent(context, PostValidateEvent.class, this);
popComponentFromEL(context);
}
/**
* <p>
* In addition to the standard <code>processUpdates</code> behavior inherited from
* {@link javax.faces.component.UIComponentBase}, calls <code>updateModel()</code>. If the component is invalid afterwards,
* calls {@link FacesContext#renderResponse}. If a <code>RuntimeException</code> is thrown during update processing, calls
* {@link FacesContext#renderResponse} and re-throw the exception.
* </p>
*
* @throws NullPointerException {@inheritDoc}
*/
@Override
public void processUpdates(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, null);
final String activeItem = getActiveItemValue();
this.visit(this, new TogglePanelCallback() {
@Override
public DataVisitResult callback(FacesContext context, TogglePanelCoordinates coords) {
AbstractTogglePanelItemInterface item = coords.getItem();
if (item instanceof AbstractTab && (isActiveItem(item, activeItem) || getSwitchType() == SwitchType.client)) {
((UIComponent) item).processUpdates(context);
}
return DataVisitResult.CONTINUE;
}
});
popComponentFromEL(context);
if (!isValid()) {
context.renderResponse();
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
@Override
public void decode(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Force validity back to "true"
setValid(true);
super.decode(context);
}
public void updateModel(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
if (!isValid() || !isLocalValueSet()) {
return;
}
ValueExpression ve = getValueExpression("value");
if (ve == null) {
return;
}
Throwable caught = null;
FacesMessage message = null;
try {
ve.setValue(context.getELContext(), getLocalValue());
setValue(null);
setLocalValueSet(false);
} catch (ELException e) {
caught = e;
String messageStr = e.getMessage();
Throwable result = e.getCause();
while (null != result && result.getClass().isAssignableFrom(ELException.class)) {
messageStr = result.getMessage();
result = result.getCause();
}
if (messageStr == null) {
message = ServiceTracker.getService(MessageFactory.class).createMessage(context, FacesMessage.SEVERITY_ERROR,
FacesMessages.UIINPUT_UPDATE, MessageUtil.getLabel(context, this));
} else {
message = new FacesMessage(FacesMessage.SEVERITY_ERROR, messageStr, messageStr);
}
setValid(false);
} catch (Exception e) {
caught = e;
// message = MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
// MessageFactory.getHeader(context, this));
setValid(false);
}
if (caught != null) {
assert message != null;
@SuppressWarnings({ "ThrowableInstanceNeverThrown" })
UpdateModelException toQueue = new UpdateModelException(message, caught);
ExceptionQueuedEventContext eventContext = new ExceptionQueuedEventContext(context, toQueue, this,
PhaseId.UPDATE_MODEL_VALUES);
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, eventContext);
}
}
private ItemChangeEvent createItemChangeEvent(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Submitted value == null means "the component was not submitted at all".
String activeItem = getSubmittedActiveItem();
if (activeItem == null) {
return null;
}
String previous = (String) getValue();
if (previous == null || !previous.equalsIgnoreCase(activeItem)) {
UIComponent prevComp = null;
UIComponent actvComp = null;
if (previous != null) {
prevComp = (UIComponent) getItem(previous);
}
if (activeItem != null) {
actvComp = (UIComponent) getItem(activeItem);
}
return new ItemChangeEvent(this, previous, prevComp, activeItem, actvComp);
}
return null;
}
@Override
public void queueEvent(FacesEvent event) {
if ((event instanceof ItemChangeEvent) && (event.getComponent() == this)) {
setEventPhase((ItemChangeEvent) event);
}
super.queueEvent(event);
}
protected void setEventPhase(ItemChangeEvent event) {
if (isImmediate()
|| (event.getNewItem() != null && RendererUtils.getInstance().isBooleanAttribute(event.getNewItem(),
"immediate"))) {
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else {
event.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
}
}
protected void setEventPhase(FacesEvent event) {
if (isImmediate()) {
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
if (event instanceof ItemChangeEvent) {
setValue(((ItemChangeEvent) event).getNewItemName());
setSubmittedActiveItem(null);
if (event.getPhaseId() == PhaseId.UPDATE_MODEL_VALUES) {
try {
updateModel(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
} else {
context.renderResponse();
}
}
super.broadcast(event);
}
// -------------------------------------------------- Panel Items Managing
@Override
public String getFamily() {
return COMPONENT_FAMILY;
}
@Override
public boolean getRendersChildren() {
return true;
}
private String getActiveItemValue() {
String value = getActiveItem();
if (value == null) {
value = getSubmittedActiveItem();
}
return value;
}
protected boolean isActiveItem(UIComponent kid) {
return isActiveItem(kid, getActiveItemValue());
}
protected boolean isActiveItem(UIComponent kid, String value) {
if (kid == null || ! (kid instanceof AbstractTogglePanelItemInterface)) {
return false;
}
return isActiveItem((AbstractTogglePanelItemInterface) kid, value);
}
protected boolean isActiveItem(AbstractTogglePanelItemInterface item, String value) {
if (item == null || value == null) {
return false;
}
return item.getName().equals(value);
}
public TogglePanelCoordinates visit(final AbstractTogglePanel panel, final TogglePanelCallback callback) {
int count = 0;
for (UIComponent child : panel.getChildren()) {
if (child.isRendered()) {
if (child instanceof AbstractTogglePanelItemInterface) {
AbstractTogglePanelItemInterface item = (AbstractTogglePanelItemInterface) child;
TogglePanelCoordinates coords = new TogglePanelCoordinates(item.getName(), count, item, true);
if (callback.callback(getFacesContext(), coords) == DataVisitResult.STOP) {
return coords;
} else {
count++;
}
} else if (child instanceof UISequence) {
final UISequence sequence = (UISequence) child;
final int[] countBox = {count};
final TogglePanelCoordinates[] coordBox = new TogglePanelCoordinates[1];
final String[] nameBox = {""};
DataVisitor visitor = new DataVisitor() {
public DataVisitResult process(FacesContext context, Object rowKey, Object argument) {
sequence.setRowKey(context, rowKey);
if (sequence.isRowAvailable()) {
if (sequence.getChildCount() > 0) {
for (UIComponent child : sequence.getChildren()) {
if ((child.isRendered()) && child instanceof AbstractTogglePanelItemInterface) {
AbstractTogglePanelItemInterface item = (AbstractTogglePanelItemInterface) child;
TogglePanelCoordinates coords = new TogglePanelCoordinates(item.getName(), countBox[0], item, false);
if (callback.callback(context, coords) == DataVisitResult.STOP) {
coordBox[0] = coords;
return DataVisitResult.STOP;
} else {
countBox[0]++;
}
}
}
}
}
return DataVisitResult.CONTINUE;
}
};
FacesContext context = getFacesContext();
try {
sequence.walk(context, visitor, null);
} finally {
sequence.setRowKey(context, null);
}
if (coordBox[0] != null) {
return coordBox[0];
}
count = countBox[0];
}
}
}
return new TogglePanelCoordinates(null, count, null, false);
}
private TogglePanelCoordinates getCoordsByIndex(final int index) {
TogglePanelCoordinates coords = visit(this, new TogglePanelCallback() {
@Override
public DataVisitResult callback(FacesContext context, TogglePanelCoordinates coords) {
if (index == coords.getCount()) {
return DataVisitResult.STOP;
}
else {
return DataVisitResult.CONTINUE;
}
}
});
return coords;
}
public TogglePanelCoordinates getCoordsByName(final String name) {
TogglePanelCoordinates coords = visit(this, new TogglePanelCallback() {
@Override
public DataVisitResult callback(FacesContext context, TogglePanelCoordinates coords) {
if (name.equals(coords.getName())) {
return DataVisitResult.STOP;
}
else {
return DataVisitResult.CONTINUE;
}
}
});
return coords;
}
public AbstractTogglePanelItemInterface getItemByIndex(final int index) {
TogglePanelCoordinates coords = getCoordsByIndex(index);
if (! coords.isConcrete()) {
LOG.warn("Warning, calling getItemByIndex on a dynamically generated TogglePanel can result in unpredictable behavior");
}
return coords.getItem();
}
public String getNameByIndex(final int index) {
if (! this.isRendered()) {
return null;
}
return getCoordsByIndex(index).getName();
}
public int getIndexByName(final String name) {
if (! this.isRendered()) {
return -1;
}
TogglePanelCoordinates coords = getCoordsByName(name);
if (coords.getName() != null) {
return coords.getCount();
} else {
return -1;
}
}
public int getItemCount() {
if (! this.isRendered()) {
return 0;
}
TogglePanelCoordinates coords = visit(this, new TogglePanelCallback() {
@Override
public DataVisitResult callback(FacesContext context, TogglePanelCoordinates coords) {
return DataVisitResult.CONTINUE;
}
});
return coords.getCount();
}
public AbstractTogglePanelItemInterface getItem(String name) {
if (META_NAME_FIRST.equals(name)) {
return getFirstItem();
} else if (META_NAME_PREV.equals(name)) {
return getPrevItem();
} else if (META_NAME_NEXT.equals(name)) {
return getNextItem();
} else if (META_NAME_LAST.equals(name)) {
return getLastItem();
} else {
return getItemByIndex(getChildIndex(name));
}
}
public AbstractTogglePanelItemInterface getFirstItem() {
return getItemByIndex(0);
}
public AbstractTogglePanelItemInterface getPrevItem() {
return getPrevItem(getActiveItem());
}
public AbstractTogglePanelItemInterface getPrevItem(String name) {
return getItemByIndex(getIndexByName(name) - 1);
}
public AbstractTogglePanelItemInterface getNextItem() {
return getNextItem(getActiveItem());
}
public AbstractTogglePanelItemInterface getNextItem(String name) {
return getItemByIndex(getIndexByName(name) + 1);
}
public AbstractTogglePanelItemInterface getLastItem() {
return getItemByIndex(getItemCount());
}
@Deprecated
public int getChildIndex(String name) {
if (name == null) {
throw new IllegalArgumentException("Name is required parameter.");
}
return getIndexByName(name);
}
// ------------------------------------------------
public String getSubmittedActiveItem() {
return submittedActiveItem;
}
public void setSubmittedActiveItem(String submittedActiveItem) {
this.submittedActiveItem = submittedActiveItem;
}
// ------------------------------------------------ Properties
@Override
@Attribute(hidden = true)
public void setValue(Object value) {
super.setValue(value);
setLocalValueSet(true);
}
/**
* Holds the active panel name. This name is a reference to the name identifier of the active child
* &lt;rich:togglePanelItem&gt; component.
*/
@Attribute
public String getActiveItem() {
return (String) getValue();
}
public void setActiveItem(String value) {
setValue(value);
}
@Override
public void setValueExpression(String name, ValueExpression binding) {
if ("activeItem".equals(name)) {
super.setValueExpression("value", binding);
} else {
super.setValueExpression(name, binding);
}
}
/**
* The switch mode when a panel is activated. One of: "client", "server", "ajax". Default: "ajax"
*/
@Attribute(generate = false)
public SwitchType getSwitchType() {
SwitchType switchType = (SwitchType) getStateHelper().eval(PropertyKeys.switchType);
if (switchType == null) {
switchType = SwitchType.DEFAULT;
}
return switchType;
}
public void setSwitchType(SwitchType switchType) {
getStateHelper().put(PropertyKeys.switchType, switchType);
}
@Attribute(hidden = true)
public abstract boolean isLimitRender();
/**
* Applicable when cycling through the tabs. If "true", then when the last tab is active, cycling to next will activate the
* first tab, if "false", cycling to next will have not effect. The inverse applies for the first tab, and cycling to
* previous. Whether to Default: false
*/
@Attribute
public abstract boolean isCycledSwitching();
@Attribute(hidden = true)
public abstract Object getData();
@Attribute(hidden = true)
public abstract String getStatus();
@Attribute(hidden = true)
public abstract Object getExecute();
@Attribute(hidden = true)
public abstract Object getRender();
/**
* Occurs on the server side when an item is changed through Ajax using the server mode
*/
@Attribute
public abstract MethodExpression getItemChangeListener();
/**
* The client-side script method to be called after the item is changed.
*/
@Attribute(events = @EventName("itemchange"))
public abstract String getOnitemchange();
/**
* The client-side script method to be called before the item is changed.
*/
@Attribute(events = @EventName("beforeitemchange"))
public abstract String getOnbeforeitemchange();
// ------------------------------------------------ Event Processing Methods
public void addItemChangeListener(ItemChangeListener listener) {
addFacesListener(listener);
}
public ItemChangeListener[] getItemChangeListeners() {
return (ItemChangeListener[]) getFacesListeners(ItemChangeListener.class);
}
public void removeItemChangeListener(ItemChangeListener listener) {
removeFacesListener(listener);
}
public String resolveClientId(FacesContext facesContext, UIComponent contextComponent, String metaComponentId) {
if (ACTIVE_ITEM_META_COMPONENT.equals(metaComponentId)) {
return getClientId(facesContext) + MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR + metaComponentId;
}
return null;
}
public String substituteUnresolvedClientId(FacesContext facesContext, UIComponent contextComponent, String metaComponentId) {
return null;
}
public void encodeMetaComponent(FacesContext context, String metaComponentId) throws IOException {
((MetaComponentRenderer) getRenderer(context)).encodeMetaComponent(context, this, metaComponentId);
}
@Override
public boolean visitTree(VisitContext context, VisitCallback callback) {
if (!isVisitable(context)) {
return false;
}
FacesContext facesContext = context.getFacesContext();
pushComponentToEL(facesContext, null);
try {
VisitResult result = context.invokeVisitCallback(this, callback);
if (result == VisitResult.COMPLETE) {
return true;
}
if (result == VisitResult.ACCEPT) {
if (context instanceof ExtendedVisitContext) {
ExtendedVisitContext extendedVisitContext = (ExtendedVisitContext) context;
if (extendedVisitContext.getVisitMode() == ExtendedVisitContextMode.RENDER) {
result = extendedVisitContext.invokeMetaComponentVisitCallback(this, callback,
ACTIVE_ITEM_META_COMPONENT);
if (result == VisitResult.COMPLETE) {
return true;
}
}
}
}
if (result == VisitResult.ACCEPT) {
Iterator<UIComponent> kids = this.getFacetsAndChildren();
while (kids.hasNext()) {
boolean done = kids.next().visitTree(context, callback);
if (done) {
return true;
}
}
}
} finally {
popComponentFromEL(facesContext);
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment