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

org.primefaces.component.api.UITabPanel Maven / Gradle / Ivy

Go to download

PrimeFaces is one of the most popular UI libraries in Java EE Ecosystem and widely used by software companies, world renowned brands, banks, financial institutions, insurance companies, universities and more.

There is a newer version: 14.0.0-RC3
Show newest version
/**
 * Copyright 2009-2018 PrimeTek.
 *
 * Licensed 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.primefaces.component.api;

import java.io.IOException;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.ContextCallback;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.NamingContainer;
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.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.event.PhaseId;
import javax.faces.event.PostValidateEvent;
import javax.faces.event.PreValidateEvent;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.ResultSetDataModel;
import javax.faces.model.ScalarDataModel;
import javax.faces.render.Renderer;
import org.primefaces.component.tabview.Tab;
import org.primefaces.util.ComponentUtils;
import org.primefaces.util.Constants;
import org.primefaces.util.SharedStringBuilder;

/**
 * UITabPanel is a specialized version of UIRepeat focusing on components that repeat tabs like tabView and accordionPanel.
 * Most of the code is copied from MyFaces.
 */
public class UITabPanel extends UIPanel implements NamingContainer {

    private final static DataModel EMPTY_MODEL = new ListDataModel(Collections.emptyList());

    private static final Class OBJECT_ARRAY_CLASS = Object[].class;

    private static final Object[] LEAF_NO_STATE = new Object[]{null, null};
    
    private static final String SB_ID = UITabPanel.class.getName() + "#id";

    public enum PropertyKeys {
        value,
        var,
        size,
        varStatus,
        offset,
        step,
        dynamic,
        prependId
    }

    private Object _initialDescendantComponentState = null;

    // Holds for each row the states of the child components of this UIData.
    // Note that only "partial" component state is saved: the component fields
    // that are expected to vary between rows.
    private Map> _rowStates = new HashMap>();

    /**
     * Handle case where this table is nested inside another table. See method getDataModel for more details.
     * 

* Key: parentClientId (aka rowId when nested within a parent table) Value: DataModel */ private Map _dataModelMap = new HashMap(); // will be set to false if the data should not be refreshed at the beginning of the encode phase private boolean _isValidChilds = true; private int _end = -1; private int _count; private int _index = -1; private transient Object _origValue; private transient Object _origVarStatus; private transient FacesContext _facesContext; public int getOffset() { return (Integer) getStateHelper().eval(PropertyKeys.offset, 0); } public void setOffset(int offset) { getStateHelper().put(PropertyKeys.offset, offset); } public int getSize() { return (Integer) getStateHelper().eval(PropertyKeys.size, -1); } public void setSize(int size) { getStateHelper().put(PropertyKeys.size, size); } public int getStep() { return (Integer) getStateHelper().eval(PropertyKeys.step, 1); } public void setStep(int step) { getStateHelper().put(PropertyKeys.step, step); } public String getVar() { return (String) getStateHelper().get(PropertyKeys.var); } public void setVar(String var) { getStateHelper().put(PropertyKeys.var, var); } public String getVarStatus() { return (String) getStateHelper().get(PropertyKeys.varStatus); } public void setVarStatus(String varStatus) { getStateHelper().put(PropertyKeys.varStatus, varStatus); } public boolean isDynamic() { return (java.lang.Boolean) getStateHelper().eval(PropertyKeys.dynamic, false); } public void setDynamic(boolean _dynamic) { getStateHelper().put(PropertyKeys.dynamic, _dynamic); } public boolean isPrependId() { return (java.lang.Boolean) getStateHelper().eval(PropertyKeys.prependId, true); } public void setPrependId(boolean _prependId) { getStateHelper().put(PropertyKeys.prependId, _prependId); } protected DataModel getDataModel() { DataModel dataModel; String clientID = ""; UIComponent parent = getParent(); if (parent != null) { clientID = parent.getContainerClientId(getFacesContext()); } dataModel = _dataModelMap.get(clientID); if (dataModel == null) { dataModel = createDataModel(); _dataModelMap.put(clientID, dataModel); } return dataModel; } private DataModel createDataModel() { Object value = getValue(); if (value == null) { return EMPTY_MODEL; } else if (value instanceof DataModel) { return (DataModel) value; } else if (value instanceof List) { return new ListDataModel((List) value); } else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass())) { return new ArrayDataModel((Object[]) value); } else if (value instanceof ResultSet) { return new ResultSetDataModel((ResultSet) value); } else { return new ScalarDataModel(value); } } @Override public void setValueExpression(String name, ValueExpression binding) { if (name == null) { throw new NullPointerException("name"); } else if (name.equals("value")) { _dataModelMap.clear(); } else if (name.equals("rowIndex")) { throw new IllegalArgumentException("name " + name); } super.setValueExpression(name, binding); } public Object getValue() { return getStateHelper().eval(PropertyKeys.value); } public void setValue(Object value) { getStateHelper().put(PropertyKeys.value, value); _dataModelMap.clear(); _rowStates.clear(); _isValidChilds = true; } @Override public String getContainerClientId(FacesContext context) { if (this.isPrependId() || this.isRepeating()) { String clientId = super.getContainerClientId(context); int index = getIndex(); if (index == -1) { return clientId; } StringBuilder sb = SharedStringBuilder.get(getFacesContext(), SB_ID, clientId.length() + 4); return sb.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(index).toString(); } else { UIComponent parent = this.getParent(); while (parent != null) { if (parent instanceof NamingContainer) { return parent.getContainerClientId(context); } parent = parent.getParent(); } return null; } } private RepeatStatus _getRepeatStatus() { return new RepeatStatus(_count == 0, _index + getStep() >= getDataModel().getRowCount(), _count, _index, getOffset(), _end, getStep()); } private void _captureScopeValues() { String var = getVar(); if (var != null) { _origValue = getFacesContext().getExternalContext().getRequestMap().get(var); } String varStatus = getVarStatus(); if (varStatus != null) { _origVarStatus = getFacesContext().getExternalContext().getRequestMap().get(varStatus); } } private boolean _isIndexAvailable() { return getDataModel().isRowAvailable(); } private void _restoreScopeValues() { String var = getVar(); if (var != null) { Map attrs = getFacesContext().getExternalContext().getRequestMap(); if (_origValue != null) { attrs.put(var, _origValue); _origValue = null; } else { attrs.remove(var); } } String varStatus = getVarStatus(); if (getVarStatus() != null) { Map attrs = getFacesContext().getExternalContext().getRequestMap(); if (_origVarStatus != null) { attrs.put(varStatus, _origVarStatus); _origVarStatus = null; } else { attrs.remove(varStatus); } } } /** * Overwrite the state of the child components of this component with data previously saved by method saveDescendantComponentStates. *

* The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are not modified. */ @SuppressWarnings("unchecked") private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state, boolean restoreChildFacets) { int descendantStateIndex = -1; List stateCollection = null; if (iterateFacets && parent.getFacetCount() > 0) { Iterator childIterator = parent.getFacets().values().iterator(); while (childIterator.hasNext()) { UIComponent component = childIterator.next(); // reset the client id (see spec 3.1.6) component.setId(component.getId()); if (!component.isTransient()) { if (descendantStateIndex == -1) { stateCollection = ((List) state); descendantStateIndex = stateCollection.isEmpty() ? -1 : 0; } if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size()) { Object[] object = stateCollection.get(descendantStateIndex); if (object[0] != null && component instanceof EditableValueHolder) { ((SavedState) object[0]).restoreState((EditableValueHolder) component); } // If there is descendant state to restore, call it recursively, otherwise // it is safe to skip iteration. if (object[1] != null) { restoreDescendantComponentStates(component, restoreChildFacets, object[1], true); } else { restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true); } } else { restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true); } descendantStateIndex++; } } } if (parent.getChildCount() > 0) { for (int i = 0; i < parent.getChildCount(); i++) { UIComponent component = parent.getChildren().get(i); // reset the client id (see spec 3.1.6) component.setId(component.getId()); if (!component.isTransient()) { if (descendantStateIndex == -1) { stateCollection = ((List) state); descendantStateIndex = stateCollection.isEmpty() ? -1 : 0; } if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size()) { Object[] object = stateCollection.get(descendantStateIndex); if (object[0] != null && component instanceof EditableValueHolder) { ((SavedState) object[0]).restoreState((EditableValueHolder) component); } // If there is descendant state to restore, call it recursively, otherwise // it is safe to skip iteration. if (object[1] != null) { restoreDescendantComponentStates(component, restoreChildFacets, object[1], true); } else { restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true); } } else { restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true); } descendantStateIndex++; } } } } /** * Just call component.setId(component.getId()) to reset all client ids and ensure they will be calculated for the current row, but do not waste * time dealing with row state code. * * @param parent * @param iterateFacets * @param restoreChildFacets */ private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets, boolean restoreChildFacets) { if (iterateFacets && parent.getFacetCount() > 0) { Iterator childIterator = parent.getFacets().values().iterator(); while (childIterator.hasNext()) { UIComponent component = childIterator.next(); // reset the client id (see spec 3.1.6) component.setId(component.getId()); if (!component.isTransient()) { restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true); } } } if (parent.getChildCount() > 0) { for (int i = 0; i < parent.getChildCount(); i++) { UIComponent component = parent.getChildren().get(i); // reset the client id (see spec 3.1.6) component.setId(component.getId()); if (!component.isTransient()) { restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true); } } } } /** * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows. *

* This is very similar to the process that occurs for normal components when the view is serialized. Transient components are skipped (no state is saved * for them). *

* If there are no children then null is returned. If there are one or more children, and all children are transient then an empty collection is returned; * this will happen whenever a table contains only read-only components. *

* Otherwise a collection is returned which contains an object for every non-transient child component; that object may itself contain a collection of the * state of that child's child components. */ private Collection saveDescendantComponentStates(UIComponent parent, boolean iterateFacets, boolean saveChildFacets) { Collection childStates = null; // Index to indicate how many components has been passed without state to save. int childEmptyIndex = 0; int totalChildCount = 0; if (iterateFacets && parent.getFacetCount() > 0) { Iterator childIterator = parent.getFacets().values().iterator(); while (childIterator.hasNext()) { UIComponent child = childIterator.next(); if (!child.isTransient()) { // Add an entry to the collection, being an array of two // elements. The first element is the state of the children // of this component; the second is the state of the current // child itself. if (child instanceof EditableValueHolder) { if (childStates == null) { childStates = new ArrayList( parent.getFacetCount() + parent.getChildCount() - totalChildCount + childEmptyIndex); for (int ci = 0; ci < childEmptyIndex; ci++) { childStates.add(LEAF_NO_STATE); } } childStates.add(child.getChildCount() > 0 ? new Object[]{new SavedState((EditableValueHolder) child), saveDescendantComponentStates(child, saveChildFacets, true)} : new Object[]{new SavedState((EditableValueHolder) child), null}); } else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0)) { Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true); if (descendantSavedState == null) { if (childStates == null) { childEmptyIndex++; } else { childStates.add(LEAF_NO_STATE); } } else { if (childStates == null) { childStates = new ArrayList( parent.getFacetCount() + parent.getChildCount() - totalChildCount + childEmptyIndex); for (int ci = 0; ci < childEmptyIndex; ci++) { childStates.add(LEAF_NO_STATE); } } childStates.add(new Object[]{null, descendantSavedState}); } } else { if (childStates == null) { childEmptyIndex++; } else { childStates.add(LEAF_NO_STATE); } } } totalChildCount++; } } if (parent.getChildCount() > 0) { for (int i = 0; i < parent.getChildCount(); i++) { UIComponent child = parent.getChildren().get(i); if (!child.isTransient()) { // Add an entry to the collection, being an array of two // elements. The first element is the state of the children // of this component; the second is the state of the current // child itself. if (child instanceof EditableValueHolder) { if (childStates == null) { childStates = new ArrayList( parent.getFacetCount() + parent.getChildCount() - totalChildCount + childEmptyIndex); for (int ci = 0; ci < childEmptyIndex; ci++) { childStates.add(LEAF_NO_STATE); } } childStates.add(child.getChildCount() > 0 ? new Object[]{new SavedState((EditableValueHolder) child), saveDescendantComponentStates(child, saveChildFacets, true)} : new Object[]{new SavedState((EditableValueHolder) child), null}); } else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0)) { Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true); if (descendantSavedState == null) { if (childStates == null) { childEmptyIndex++; } else { childStates.add(LEAF_NO_STATE); } } else { if (childStates == null) { childStates = new ArrayList( parent.getFacetCount() + parent.getChildCount() - totalChildCount + childEmptyIndex); for (int ci = 0; ci < childEmptyIndex; ci++) { childStates.add(LEAF_NO_STATE); } } childStates.add(new Object[]{null, descendantSavedState}); } } else { if (childStates == null) { childEmptyIndex++; } else { childStates.add(LEAF_NO_STATE); } } } totalChildCount++; } } return childStates; } /** * Returns the rowCount of the underlying DataModel. * * @return */ public int getRowCount() { return getDataModel().getRowCount(); } /** * Returns the rowCount of the underlying DataModel. * * @return */ public Object getIndexData() { if (!getDataModel().isRowAvailable()) { return null; } return getDataModel().getRowData(); } /** * Returns the current index. */ public int getIndex() { return _index; } public void setIndex(int index) { // save child state //_saveChildState(); if (index < -1) { throw new IllegalArgumentException("rowIndex is less than -1"); } if (_index == index) { return; } FacesContext facesContext = getFacesContext(); if (_index == -1) { if (_initialDescendantComponentState == null) { // Create a template that can be used to initialise any row // that we haven't visited before, ie a "saved state" that can // be pushed to the "restoreState" method of all the child // components to set them up to represent a clean row. _initialDescendantComponentState = saveDescendantComponentStates(this, true, true); } } else { // If no initial component state, there are no EditableValueHolder instances, // and that means there is no state to be saved for the current row, so we can // skip row state saving code safely. if (_initialDescendantComponentState != null) { // We are currently positioned on some row, and are about to // move off it, so save the (partial) state of the components // representing the current row. Later if this row is revisited // then we can restore this state. Collection savedRowState = saveDescendantComponentStates(this, true, true); if (savedRowState != null) { _rowStates.put(getContainerClientId(facesContext), savedRowState); } } } _index = index; DataModel localModel = getDataModel(); localModel.setRowIndex(index); if (_index != -1) { String var = getVar(); if (var != null && localModel.isRowAvailable()) { getFacesContext().getExternalContext().getRequestMap() .put(var, localModel.getRowData()); } String varStatus = getVarStatus(); if (varStatus != null) { getFacesContext().getExternalContext().getRequestMap() .put(varStatus, _getRepeatStatus()); } } // restore child state //_restoreChildState(); if (_index == -1) { // reset components to initial state // If no initial state, skip row restore state code if (_initialDescendantComponentState != null) { restoreDescendantComponentStates(this, true, _initialDescendantComponentState, true); } else { restoreDescendantComponentWithoutRestoreState(this, true, true); } } else { Object rowState = _rowStates.get(getContainerClientId(facesContext)); if (rowState == null) { // We haven't been positioned on this row before, so just // configure the child components of this component with // the standard "initial" state // If no initial state, skip row restore state code if (_initialDescendantComponentState != null) { restoreDescendantComponentStates(this, true, _initialDescendantComponentState, true); } else { restoreDescendantComponentWithoutRestoreState(this, true, true); } } else { // We have been positioned on this row before, so configure // the child components of this component with the (partial) // state that was previously saved. Fields not in the // partial saved state are left with their original values. restoreDescendantComponentStates(this, true, rowState, true); } } } /** * Calculates the count value for the given index. * * @param index * @return */ private int _calculateCountForIndex(int index) { return (index - getOffset()) / getStep(); } private void _validateAttributes() throws FacesException { int begin = getOffset(); int end = getDataModel().getRowCount(); int size = getSize(); int step = getStep(); boolean sizeIsEnd = false; if (size == -1) { size = end; sizeIsEnd = true; } if (end >= 0) { if (size < 0) { throw new FacesException("iteration size cannot be less " + "than zero"); } else if (!sizeIsEnd && (begin + size) > end) { throw new FacesException("iteration size cannot be greater " + "than collection size"); } } if ((size > -1) && (begin > end)) { throw new FacesException("iteration offset cannot be greater " + "than collection size"); } if (step == -1) { setStep(1); } if (step < 0) { throw new FacesException("iteration step size cannot be less " + "than zero"); } else if (step == 0) { throw new FacesException("iteration step size cannot be equal " + "to zero"); } _end = size; //_step = step; } public void process(FacesContext faces, PhaseId phase) { // stop if not rendered if (!isRendered()) { return; } // validate attributes _validateAttributes(); // reset index _captureScopeValues(); setIndex(-1); try { // has children if (getChildCount() > 0) { int i = getOffset(); int end = getSize(); int step = getStep(); end = (end >= 0) ? i + end : Integer.MAX_VALUE - 1; // grab renderer String rendererType = getRendererType(); Renderer renderer = null; if (rendererType != null) { renderer = getRenderer(faces); } _count = 0; setIndex(i); while (i <= end && _isIndexAvailable()) { if (PhaseId.RENDER_RESPONSE.equals(phase) && renderer != null) { renderer.encodeChildren(faces, this); } else { for (int j = 0, childCount = getChildCount(); j < childCount; j++) { UIComponent child = getChildren().get(j); if (PhaseId.APPLY_REQUEST_VALUES.equals(phase)) { child.processDecodes(faces); } else if (PhaseId.PROCESS_VALIDATIONS.equals(phase)) { child.processValidators(faces); } else if (PhaseId.UPDATE_MODEL_VALUES.equals(phase)) { child.processUpdates(faces); } else if (PhaseId.RENDER_RESPONSE.equals(phase)) { child.encodeAll(faces); } } } ++_count; i += step; setIndex(i); } } } catch (IOException e) { throw new FacesException(e); } finally { setIndex(-1); _restoreScopeValues(); } } @Override public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException { if (!this.isRepeating()) { return super.invokeOnComponent(context, clientId, callback); } else { if (context == null || clientId == null || callback == null) { throw new NullPointerException(); } final String baseClientId = getClientId(context); // searching for this component? boolean returnValue = baseClientId.equals(clientId); boolean isCachedFacesContext = isTemporalFacesContext(); if (!isCachedFacesContext) { setTemporalFacesContext(context); } pushComponentToEL(context, this); try { if (returnValue) { try { callback.invokeContextCallback(context, this); return true; } catch (Exception e) { throw new FacesException(e); } } // Now Look throught facets on this UIComponent if (this.getFacetCount() > 0) { for (Iterator it = this.getFacets().values().iterator(); !returnValue && it.hasNext();) { returnValue = it.next().invokeOnComponent(context, clientId, callback); } } if (returnValue) { return returnValue; } // is the component an inner component? if (clientId.startsWith(baseClientId)) { // Check if the clientId for the component, which we // are looking for, has a rowIndex attached char separator = UINamingContainer.getSeparatorChar(context); String subId = clientId.substring(baseClientId.length() + 1); //If the char next to baseClientId is the separator one and //the subId matches the regular expression if (clientId.charAt(baseClientId.length()) == separator && subId.matches("[0-9]+" + separator + ".*")) { String clientRow = subId.substring(0, subId.indexOf(separator)); // safe the current index, count aside final int prevIndex = _index; final int prevCount = _count; try { int invokeIndex = Integer.parseInt(clientRow); // save the current scope values and set the right index _captureScopeValues(); if (invokeIndex != -1) { // calculate count for RepeatStatus _count = _calculateCountForIndex(invokeIndex); } setIndex(invokeIndex); if (!_isIndexAvailable()) { return false; } for (Iterator it1 = getChildren().iterator(); !returnValue && it1.hasNext();) { //recursive call to find the component returnValue = it1.next().invokeOnComponent(context, clientId, callback); } } finally { // restore the previous count, index and scope values _count = prevCount; setIndex(prevIndex); _restoreScopeValues(); } } else { // Searching for this component's children if (this.getChildCount() > 0) { // Searching for this component's children/facets for (Iterator it = this.getChildren().iterator(); !returnValue && it.hasNext();) { returnValue = it.next().invokeOnComponent(context, clientId, callback); } } } } } finally { //all components must call popComponentFromEl after visiting is finished popComponentFromEL(context); if (!isCachedFacesContext) { setTemporalFacesContext(null); } } return returnValue; } } @Override protected FacesContext getFacesContext() { if (_facesContext == null) { return super.getFacesContext(); } else { return _facesContext; } } private boolean isTemporalFacesContext() { return _facesContext != null; } private void setTemporalFacesContext(FacesContext facesContext) { _facesContext = facesContext; } @Override public boolean visitTree(VisitContext context, VisitCallback callback) { if (!this.isRepeating()) { return super.visitTree(context, callback); } else { // override the behavior from UIComponent to visit // all children once per "row" if (ComponentUtils.isSkipIteration(context, getFacesContext())) { return super.visitTree(context, callback); } if (!isVisitable(context)) { return false; } // save the current index, count aside final int prevIndex = _index; final int prevCount = _count; // validate attributes _validateAttributes(); // reset index and save scope values _captureScopeValues(); setIndex(-1); // push the Component to EL pushComponentToEL(context.getFacesContext(), this); try { VisitResult res = context.invokeVisitCallback(this, callback); switch (res) { // we are done, nothing has to be processed anymore case COMPLETE: return true; case REJECT: return false; //accept default: // determine if we need to visit our children // Note that we need to do this check because we are a NamingContainer Collection subtreeIdsToVisit = context .getSubtreeIdsToVisit(this); boolean doVisitChildren = subtreeIdsToVisit != null && !subtreeIdsToVisit.isEmpty(); if (doVisitChildren) { // visit the facets of the component if (getFacetCount() > 0) { for (UIComponent facet : getFacets().values()) { if (facet.visitTree(context, callback)) { return true; } } } // visit the children once per "row" if (getChildCount() > 0) { int i = getOffset(); int end = getSize(); int step = getStep(); end = (end >= 0) ? i + end : Integer.MAX_VALUE - 1; _count = 0; setIndex(i); while (i <= end && _isIndexAvailable()) { for (int j = 0, childCount = getChildCount(); j < childCount; j++) { UIComponent child = getChildren().get(j); if (child.visitTree(context, callback)) { return true; } } _count++; i += step; setIndex(i); } } } return false; } } finally { // pop the component from EL popComponentFromEL(context.getFacesContext()); // restore the previous count, index and scope values _count = prevCount; setIndex(prevIndex); _restoreScopeValues(); } } } @Override public void processDecodes(FacesContext context) { if (!isRendered()) { return; } pushComponentToEL(context, null); if (!shouldSkipChildren(context)) { if (this.isRepeating()) { process(context, PhaseId.APPLY_REQUEST_VALUES); } else { if (this.isDynamic()) { for (UIComponent component : getChildren()) { if (component instanceof Tab) { Tab tab = (Tab) component; if (tab.isLoaded()) { tab.processDecodes(context); } } } } else { ComponentUtils.processDecodesOfFacetsAndChilds(this, context); } } } try { decode(context); } catch (RuntimeException e) { context.renderResponse(); throw e; } finally { popComponentFromEL(context); } } @Override public void processValidators(FacesContext context) { if (!isRendered()) { return; } if (shouldSkipChildren(context)) { return; } pushComponentToEL(context, null); Application app = context.getApplication(); app.publishEvent(context, PreValidateEvent.class, this); if (this.isRepeating()) { process(context, PhaseId.PROCESS_VALIDATIONS); } else { if (this.isDynamic()) { for (UIComponent component : getChildren()) { if (component instanceof Tab) { Tab tab = (Tab) component; if (tab.isLoaded()) { tab.processValidators(context); } } } } else { ComponentUtils.processValidatorsOfFacetsAndChilds(this, context); } } // check if an validation error forces the render response for our data if (context.getRenderResponse()) { _isValidChilds = false; } app.publishEvent(context, PostValidateEvent.class, this); popComponentFromEL(context); } @Override public void processUpdates(FacesContext context) { if (!isRendered()) { return; } if (shouldSkipChildren(context)) { return; } pushComponentToEL(context, null); if (this.isRepeating()) { process(context, PhaseId.UPDATE_MODEL_VALUES); } else { if (this.isDynamic()) { for (UIComponent component : getChildren()) { if (component instanceof Tab) { Tab tab = (Tab) component; if (tab.isLoaded()) { tab.processUpdates(context); } } } } else { ComponentUtils.processUpdatesOfFacetsAndChilds(this, context); } } if (context.getRenderResponse()) { _isValidChilds = false; } popComponentFromEL(context); } // from RI private final static class SavedState implements Serializable { private boolean _localValueSet; private Object _submittedValue; private boolean _valid = true; private Object _value; private static final long serialVersionUID = 2920252657338389849L; public SavedState(EditableValueHolder evh) { _value = evh.getLocalValue(); _localValueSet = evh.isLocalValueSet(); _valid = evh.isValid(); _submittedValue = evh.getSubmittedValue(); } Object getSubmittedValue() { return (_submittedValue); } void setSubmittedValue(Object submittedValue) { _submittedValue = submittedValue; } boolean isValid() { return (_valid); } void setValid(boolean valid) { _valid = valid; } Object getValue() { return _value; } public void setValue(Object value) { _value = value; } boolean isLocalValueSet() { return _localValueSet; } public void setLocalValueSet(boolean localValueSet) { _localValueSet = localValueSet; } @Override public String toString() { return ("submittedValue: " + _submittedValue + " value: " + _value + " localValueSet: " + _localValueSet); } public void restoreState(EditableValueHolder evh) { evh.setValue(_value); evh.setValid(_valid); evh.setSubmittedValue(_submittedValue); evh.setLocalValueSet(_localValueSet); } public void populate(EditableValueHolder evh) { _value = evh.getLocalValue(); _valid = evh.isValid(); _submittedValue = evh.getSubmittedValue(); _localValueSet = evh.isLocalValueSet(); } public void apply(EditableValueHolder evh) { evh.setValue(_value); evh.setValid(_valid); evh.setSubmittedValue(_submittedValue); evh.setLocalValueSet(_localValueSet); } } private final class IndexedEvent extends FacesEvent { private final FacesEvent _target; private final int _index; public IndexedEvent(UITabPanel owner, FacesEvent target, int index) { super(owner); _target = target; _index = index; } @Override public PhaseId getPhaseId() { return _target.getPhaseId(); } @Override public void setPhaseId(PhaseId phaseId) { _target.setPhaseId(phaseId); } public boolean isAppropriateListener(FacesListener listener) { return _target.isAppropriateListener(listener); } public void processListener(FacesListener listener) { UITabPanel owner = (UITabPanel) getComponent(); // safe the current index, count aside final int prevIndex = owner._index; final int prevCount = owner._count; try { owner._captureScopeValues(); if (this._index != -1) { // calculate count for RepeatStatus _count = _calculateCountForIndex(this._index); } owner.setIndex(this._index); if (owner._isIndexAvailable()) { _target.processListener(listener); } } finally { // restore the previous count, index and scope values owner._count = prevCount; owner.setIndex(prevIndex); owner._restoreScopeValues(); } } public int getIndex() { return _index; } public FacesEvent getTarget() { return _target; } } @Override public void broadcast(FacesEvent event) throws AbortProcessingException { if (!this.isRepeating()) { super.broadcast(event); } else { if (event instanceof IndexedEvent) { IndexedEvent idxEvent = (IndexedEvent) event; // safe the current index, count aside final int prevIndex = _index; final int prevCount = _count; try { _captureScopeValues(); if (idxEvent.getIndex() != -1) { // calculate count for RepeatStatus _count = _calculateCountForIndex(idxEvent.getIndex()); } setIndex(idxEvent.getIndex()); if (_isIndexAvailable()) { // get the target FacesEvent FacesEvent target = idxEvent.getTarget(); FacesContext facesContext = getFacesContext(); // get the component associated with the target event and push // it and its composite component parent, if available, to the // component stack to have them available while processing the // event (see also UIViewRoot._broadcastAll()). UIComponent targetComponent = target.getComponent(); UIComponent compositeParent = UIComponent .getCompositeComponentParent(targetComponent); if (compositeParent != null) { pushComponentToEL(facesContext, compositeParent); } pushComponentToEL(facesContext, targetComponent); try { // actual event broadcasting targetComponent.broadcast(target); } finally { // remove the components from the stack again popComponentFromEL(facesContext); if (compositeParent != null) { popComponentFromEL(facesContext); } } } } finally { // restore the previous count, index and scope values _count = prevCount; setIndex(prevIndex); _restoreScopeValues(); } } else { super.broadcast(event); } } } @Override public void queueEvent(FacesEvent event) { if (!this.isRepeating()) { super.queueEvent(event); } else { super.queueEvent(new IndexedEvent(this, event, _index)); } } // -=Leonardo Uribe=- At the moment I haven't found any use case that // require to store the rowStates in the component state, mostly // because EditableValueHolder instances render the value into the // client and then this value are taken back at the beginning of the // next request. So, I just let this code in comments just in case // somebody founds an issue with this. /* @SuppressWarnings("unchecked") @Override public void restoreState(FacesContext facesContext, Object state) { if (state == null) { return; } Object[] values = (Object[])state; super.restoreState(facesContext,values[0]); if (values[1] == null) { _rowStates.clear(); } else { _rowStates = (Map>) restoreAttachedState(facesContext, values[1]); } } @Override public Object saveState(FacesContext facesContext) { if (initialStateMarked()) { Object parentSaved = super.saveState(facesContext); if (parentSaved == null && _rowStates.isEmpty()) { //No values return null; } return new Object[]{parentSaved, saveAttachedState(facesContext, _rowStates)}; } else { Object[] values = new Object[2]; values[0] = super.saveState(facesContext); values[1] = saveAttachedState(facesContext, _rowStates); return values; } } */ @Override public void encodeBegin(FacesContext context) throws IOException { _initialDescendantComponentState = null; if (_isValidChilds && !hasErrorMessages(context)) { // Clear the data model so that when rendering code calls // getDataModel a fresh model is fetched from the backing // bean via the value-binding. _dataModelMap.clear(); // When the data model is cleared it is also necessary to // clear the saved row state, as there is an implicit 1:1 // relation between objects in the _rowStates and the // corresponding DataModel element. _rowStates.clear(); } super.encodeBegin(context); } private boolean hasErrorMessages(FacesContext context) { for (Iterator iter = context.getMessages(); iter.hasNext();) { FacesMessage message = iter.next(); if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0) { return true; } } return false; } @Override public void encodeChildren(FacesContext faces) throws IOException { if (!isRendered()) { return; } process(faces, PhaseId.RENDER_RESPONSE); } @Override public boolean getRendersChildren() { if (getRendererType() != null) { Renderer renderer = getRenderer(getFacesContext()); if (renderer != null) { return renderer.getRendersChildren(); } } return true; } public boolean isRepeating() { return (this.getVar() != null); } public void resetLoadedTabsState() { if (!this.isRepeating() && this.isDynamic()) { for (UIComponent component : getChildren()) { if (component instanceof Tab) { Tab tab = (Tab) component; tab.setLoaded(false); } } } } protected boolean shouldSkipChildren(FacesContext context) { if (!ComponentUtils.isRequestSource(this, context)) { return false; } Map params = context.getExternalContext().getRequestParameterMap(); String paramValue = params.get(Constants.RequestParams.SKIP_CHILDREN_PARAM); if (ComponentUtils.isValueBlank(paramValue)) { return true; } return Boolean.valueOf(paramValue); } }