/*
* 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.view.facelets.component;
import java.io.IOException;
import java.io.Serializable;
import java.sql.ResultSet;
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.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.UIComponentBase;
import javax.faces.component.UIData;
import javax.faces.component.UINamingContainer;
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.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.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
/**
* TODO: PartialStateSaving and pluginize this component!
*/
@JSFComponent(name="ui:repeat", defaultRendererType="facelets.ui.Repeat")
public class UIRepeat extends UIComponentBase implements NamingContainer
{
public static final String COMPONENT_TYPE = "facelets.ui.Repeat";
public static final String COMPONENT_FAMILY = "facelets";
private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
private final static DataModel<?> EMPTY_MODEL = new ListDataModel<Object>(Collections.emptyList());
private final static SavedState NULL_STATE = new SavedState();
private Map<String, SavedState> _childState;
// our data
private Object _value;
// variables
private String _var;
private int _end = -1;
private int _count;
private int _index = -1;
// scoping
private int _offset = -1;
private int _size = -1;
private int _step = -1;
private String _varStatus;
private transient StringBuffer _buffer;
private transient DataModel<?> _model;
private transient Object _origValue;
private transient Object _origVarStatus;
private transient FacesContext _facesContext;
public UIRepeat()
{
setRendererType("facelets.ui.Repeat");
}
public String getFamily()
{
return COMPONENT_FAMILY;
}
@JSFProperty
public int getOffset()
{
if (_offset != -1)
{
return _offset;
}
ValueExpression ve = getValueExpression("offset");
if (ve != null)
{
return ((Integer)ve.getValue(getFacesContext().getELContext())).intValue();
}
return 0;
}
public void setOffset(int offset)
{
_offset = offset;
}
@JSFProperty
public int getSize()
{
if (_size != -1)
{
return _size;
}
ValueExpression ve = getValueExpression("size");
if (ve != null)
{
return ((Integer)ve.getValue(getFacesContext().getELContext())).intValue();
}
return -1;
}
public void setSize(int size)
{
_size = size;
}
@JSFProperty
public int getStep()
{
if (_step != -1)
{
return _step;
}
ValueExpression ve = getValueExpression("step");
if (ve != null)
{
return ((Integer)ve.getValue(getFacesContext().getELContext())).intValue();
}
return 1;
}
public void setStep(int step)
{
_step = step;
}
@JSFProperty
public String getVar()
{
return _var;
}
public void setVar(String var)
{
_var = var;
}
@JSFProperty
public String getVarStatus ()
{
return _varStatus;
}
public void setVarStatus (String varStatus)
{
_varStatus = varStatus;
}
private synchronized void setDataModel(DataModel<?> model)
{
_model = model;
}
@SuppressWarnings("unchecked")
private synchronized DataModel<?> getDataModel()
{
if (_model == null)
{
Object val = getValue();
if (val == null)
{
_model = EMPTY_MODEL;
}
else if (val instanceof DataModel)
{
_model = (DataModel<?>) val;
}
else if (val instanceof List)
{
_model = new ListDataModel<Object>((List<Object>) val);
}
else if (Object[].class.isAssignableFrom(val.getClass()))
{
_model = new ArrayDataModel<Object>((Object[]) val);
}
else if (val instanceof ResultSet)
{
_model = new ResultSetDataModel((ResultSet) val);
}
else
{
_model = new ScalarDataModel(val);
}
}
return _model;
}
@JSFProperty
public Object getValue()
{
if (_value == null)
{
ValueExpression ve = getValueExpression("value");
if (ve != null)
{
return ve.getValue(getFacesContext().getELContext());
}
}
return _value;
}
public void setValue(Object value)
{
_value = value;
}
/*
@Override
public String getClientId(FacesContext faces)
{
String id = super.getClientId(faces);
if (_index >= 0)
{
id = _getBuffer().append(id).append(UINamingContainer.getSeparatorChar(faces)).append(_index).toString();
}
return id;
}*/
@Override
public String getContainerClientId(FacesContext faces)
{
String id = super.getContainerClientId(faces);
if (_index >= 0)
{
id = _getBuffer().append(id).append(UINamingContainer.getSeparatorChar(faces)).append(_index).toString();
}
return id;
}
private RepeatStatus _getRepeatStatus()
{
return new RepeatStatus(_count == 0, _index + getStep() >= getDataModel().getRowCount(),
_count, _index, getOffset(), _end, getStep());
}
private void _captureScopeValues()
{
if (_var != null)
{
_origValue = getFacesContext().getExternalContext().getRequestMap().get(_var);
}
if (_varStatus != null)
{
_origVarStatus = getFacesContext().getExternalContext().getRequestMap().get(_varStatus);
}
}
private StringBuffer _getBuffer()
{
if (_buffer == null)
{
_buffer = new StringBuffer();
}
_buffer.setLength(0);
return _buffer;
}
private Map<String, SavedState> _getChildState()
{
if (_childState == null)
{
_childState = new HashMap<String, SavedState>();
}
return _childState;
}
private boolean _isIndexAvailable()
{
return getDataModel().isRowAvailable();
}
private boolean _isNestedInIterator()
{
UIComponent parent = getParent();
while (parent != null)
{
if (parent instanceof UIData || parent instanceof UIRepeat)
{
return true;
}
parent = parent.getParent();
}
return false;
}
private boolean _keepSaved(FacesContext context)
{
for (String clientId : _getChildState().keySet())
{
// Perf: messages are instances of arrayList (or Collections.emptyList): see Method org.apache.myfaces.context.servlet.FacesContextImpl.addMessage(String, FacesMessage)
List<FacesMessage> messageList = context.getMessageList(clientId);
for (int i = 0, size = messageList.size(); i < size; i++)
{
FacesMessage message = messageList.get(i);
if (message.getSeverity().compareTo(FacesMessage.SEVERITY_ERROR) >= 0)
{
return true;
}
}
}
return _isNestedInIterator();
}
private void _resetDataModel()
{
if (_isNestedInIterator())
{
setDataModel(null);
}
}
private void _restoreScopeValues()
{
if (_var != null)
{
Map<String, Object> attrs = getFacesContext().getExternalContext().getRequestMap();
if (_origValue != null)
{
attrs.put(_var, _origValue);
_origValue = null;
}
else
{
attrs.remove(_var);
}
}
if (_varStatus != null)
{
Map<String, Object> attrs = getFacesContext().getExternalContext().getRequestMap();
if (_origVarStatus != null)
{
attrs.put(_varStatus, _origVarStatus);
_origVarStatus = null;
}
else
{
attrs.remove(_varStatus);
}
}
}
private void _restoreChildState()
{
if (getChildCount() > 0)
{
FacesContext context = getFacesContext();
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
_restoreChildState(context, child);
}
}
}
private void _restoreChildState(FacesContext faces, UIComponent c)
{
// reset id
String id = c.getId();
c.setId(id);
// hack
if (c instanceof EditableValueHolder)
{
EditableValueHolder evh = (EditableValueHolder) c;
String clientId = c.getClientId(faces);
SavedState ss = _getChildState().get(clientId);
if (ss != null)
{
ss.apply(evh);
}
else
{
NULL_STATE.apply(evh);
}
}
// continue hack
if (c.getFacetCount() > 0)
{
for (UIComponent facet : c.getFacets().values())
{
_restoreChildState(faces, facet);
}
}
int childCount = c.getChildCount();
if (childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
UIComponent child = c.getChildren().get(i);
_restoreChildState(faces, child);
}
}
}
private void _saveChildState()
{
if (getChildCount() > 0)
{
FacesContext context = getFacesContext();
for (int i = 0, childCount = getChildCount(); i < childCount; i++)
{
UIComponent child = getChildren().get(i);
_saveChildState(context, child);
}
}
}
private void _saveChildState(FacesContext faces, UIComponent c)
{
if (c instanceof EditableValueHolder && !c.isTransient())
{
String clientId = c.getClientId(faces);
SavedState ss = (SavedState) _getChildState().get(clientId);
if (ss == null)
{
ss = new SavedState();
_getChildState().put(clientId, ss);
}
ss.populate((EditableValueHolder) c);
}
// continue hack
if (c.getFacetCount() > 0)
{
for (UIComponent facet : c.getFacets().values())
{
_saveChildState(faces, facet);
}
}
int childCount = c.getChildCount();
if (childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
UIComponent child = c.getChildren().get(i);
_saveChildState(faces, child);
}
}
}
/**
* Returns the rowCount of the underlying DataModel.
* @return
*/
public int getRowCount()
{
return getDataModel().getRowCount();
}
/**
* Returns the current index.
*/
public int getIndex()
{
return _index;
}
private void _setIndex(int index)
{
// save child state
_saveChildState();
_index = index;
DataModel<?> localModel = getDataModel();
localModel.setRowIndex(index);
if (_index != -1)
{
if (_var != null && localModel.isRowAvailable())
{
getFacesContext().getExternalContext().getRequestMap()
.put(_var, localModel.getRowData());
}
if (_varStatus != null)
{
getFacesContext().getExternalContext().getRequestMap()
.put(_varStatus, _getRepeatStatus());
}
}
// restore child state
_restoreChildState();
}
/**
* 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)
{
step = 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();
// clear datamodel
_resetDataModel();
// 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 (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<UIComponent> 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<UIComponent> 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<UIComponent> 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)
{
// override the behavior from UIComponent to visit
// all children once per "row"
Boolean skipIterationHint = (Boolean) context.getFacesContext().getAttributes().get(SKIP_ITERATION_HINT);
if (skipIterationHint != null && skipIterationHint.booleanValue())
{
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();
// clear datamodel
_resetDataModel();
// 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<String> 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 faces)
{
if (!isRendered())
{
return;
}
setDataModel(null);
if (!_keepSaved(faces))
{
_childState = null;
}
process(faces, PhaseId.APPLY_REQUEST_VALUES);
decode(faces);
}
@Override
public void processUpdates(FacesContext faces)
{
if (!isRendered())
{
return;
}
_resetDataModel();
process(faces, PhaseId.UPDATE_MODEL_VALUES);
}
@Override
public void processValidators(FacesContext faces)
{
if (!isRendered())
{
return;
}
_resetDataModel();
process(faces, PhaseId.PROCESS_VALIDATIONS);
}
// 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;
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 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(UIRepeat 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)
{
UIRepeat owner = (UIRepeat) 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 (event instanceof IndexedEvent)
{
IndexedEvent idxEvent = (IndexedEvent) event;
_resetDataModel();
// 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)
{
super.queueEvent(new IndexedEvent(this, event, _index));
}
@SuppressWarnings("unchecked")
@Override
public void restoreState(FacesContext faces, Object object)
{
Object[] state = (Object[]) object;
super.restoreState(faces, state[0]);
_childState = (Map<String, SavedState>) state[1];
_offset = ((Integer) state[2]).intValue();
_size = ((Integer) state[3]).intValue();
_var = (String) state[4];
_value = state[5];
_varStatus = (String) state[6];
}
@Override
public Object saveState(FacesContext faces)
{
Object[] state = new Object[7];
state[0] = super.saveState(faces);
state[1] = _childState;
state[2] = Integer.valueOf(_offset);
state[3] = Integer.valueOf(_size);
state[4] = _var;
state[5] = _value;
state[6] = _varStatus;
return state;
}
@Override
public void encodeChildren(FacesContext faces) throws IOException
{
if (!isRendered())
{
return;
}
setDataModel(null);
if (!_keepSaved(faces))
{
_childState = null;
}
process(faces, PhaseId.RENDER_RESPONSE);
}
@Override
public boolean getRendersChildren()
{
if (getRendererType() != null)
{
Renderer renderer = getRenderer(getFacesContext());
if (renderer != null)
{
return renderer.getRendersChildren();
}
}
return true;
}
}