org.richfaces.context.ExtendedPartialViewContext Maven / Gradle / Ivy
Show all versions of richfaces-core Show documentation
/*
* JBoss, Home of Professional Open Source
* Copyright 2013, 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.context;
import static org.richfaces.renderkit.AjaxConstants.AJAX_COMPONENT_ID_PARAMETER;
import static org.richfaces.renderkit.AjaxConstants.ALL;
import static org.richfaces.renderkit.AjaxConstants.BEHAVIOR_EVENT_PARAMETER;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.faces.FactoryFinder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitContextFactory;
import javax.faces.component.visit.VisitHint;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialResponseWriter;
import javax.faces.context.PartialViewContext;
import javax.faces.context.PartialViewContextWrapper;
import javax.faces.event.PhaseId;
import org.ajax4jsf.component.AjaxOutput;
import org.ajax4jsf.javascript.ScriptUtils;
import org.richfaces.application.ServiceTracker;
import org.richfaces.javascript.JavaScriptService;
import org.richfaces.javascript.ScriptsHolder;
import org.richfaces.renderkit.AjaxDataSerializer;
import org.richfaces.renderkit.HtmlConstants;
import org.richfaces.util.FastJoiner;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
/**
*
* The RichFaces custom version of PartialViewContext
*
*
* Important differences of RichFaces implementation:
*
*
* - Values are resolved in runtime by visiting the request activator component and evaluating attributes
* - Usage of extended visit contexts in order to support meta-components processing
* - Support for auto-updateable Ajax components
* - Support for Ajax extensions like passing data to the client, onbeforedomupdate and oncomplete callbacks and {@link JavaScriptService}
*
*
*
* Extended Partial View Processing Scheme
*
* On following diagram, you can see which classes contributes to RichFaces specific partial view processing (find an explanation bellow):
*
*
* +-------------------------+ RF: ExtendedPartialViewContextFactory
* |PartialViewContextFactory|
* +------------+------------+ +------------------------+
* | | PartialViewContext | Mojarra: PartialViewContextImpl
* +--------------->| | RF: ExtendedPartialViewContext
* creates |#processPartial(PhaseId)|
* +--+-------------+-------+
* | |
* RF: ExtendedVisitContextFactory | |
* +-------------------+ | |
* |VisitContextFactory| | |
* +--------------+----+ uses | |creates/uses
* | | |
* creates | | |
* v v v
* +----------------------------+ +---------------------------+
* | VisitContext | | VisitCallback |
* | | | |
* | #invokeVisitContext | | #visit |
* |(UIComponent, VisitCallback)| |(VisitContext, UIComponent)|
* +----------------------------+ +---------------------------+
* Mojarra: PhaseAwareVisitCallback
* RF: ExtendedExecuteVisitContext RF: MetaComponentProcessingVisitCallback
* RF: ExtendedRenderVisitContext RF: MetaComponentEncodingVisitCallback
*
* +-----------------------------+
* | UIViewRoot |
* | |
* | #visitTree |
* |(VisitContext, VisitCallback)|
* +-----------------------------+
*
*
* {@link ExtendedPartialViewContext} does (except other things) tracking of the mode ({@link ExtendedVisitContextMode}). This mode determines whether we are not inside a partial processing phase (inside {@link #processPartial(PhaseId)}) or not (determined by mode equal to null).
*
* The knowledge of current mode is leveraged by {@link ExtendedVisitContextFactory} that either use visit context created by parent factory (in a wrapper chain) in case the mode is equal to null or it creates {@link ExtendedExecuteVisitContext} (resp. {@link ExtendedRenderVisitContext}).
*
* (Note: the null mode is necessary because the tree visiting can be leveraged outside of partial tree processing/rendering, e.g. in partial state saving)
*
*
These {@link VisitContext} wrappers makes sure that when {@link VisitContext#invokeVisitCallback(UIComponent, VisitCallback)} method is called, the actual {@link VisitCallback} created by JSF implementation is wrapped in wrappers that allows to execute (resp. render) meta-components {@link MetaComponentProcessingVisitCallback} (resp. {@link MetaComponentEncodingVisitCallback}).
*
* While extended {@link VisitContext} implementations ({@link ExtendedRenderVisitContext} and {@link ExtendedExecuteVisitContext}) allows to visit subtrees that are not normally visited (meta-components and implicitly renderer areas, aka {@link AjaxOutput}s),
*
* extended implementations of {@link VisitCallback} ({@link MetaComponentProcessingVisitCallback} and {@link MetaComponentEncodingVisitCallback}) do the extended processing and rendering logic for meta-components.
*
* {@link UIViewRoot} is a place where the tree processing starts to dive into subtrees, it is called by JSF implementation of {@link PartialViewContext#processPartial(PhaseId)}.
*
* Rendering AJAX Extensions
*
* This context returns wrapped {@link PartialResponseWriter} in order to intercept its {@link PartialResponseWriter#endDocument()} method and write extensions before the document is actually ended. For more details see {@link ExtensionWritingPartialResponseWriter}.
*
* @author Lukas Fryc
* @author Nick Belaevski
*/
public class ExtendedPartialViewContext extends PartialViewContextWrapper {
private static final String EXTENSION_ID = "org.richfaces.extension";
private static final String BEFOREDOMUPDATE_ELEMENT_NAME = "beforedomupdate";
private static final String COMPLETE_ELEMENT_NAME = "complete";
private static final String RENDER_ELEMENT_NAME = "render";
private static final String DATA_ELEMENT_NAME = "data";
private static final String COMPONENT_DATA_ELEMENT_NAME = "componentData";
private static final FastJoiner SPACE_JOINER = FastJoiner.on(' ');
private static final String ATTRIBUTE_NAME = ExtendedPartialViewContext.class.getName();
private FacesContext facesContext;
private PartialViewContext wrappedViewContext;
private PartialResponseWriter partialResponseWriter;
private boolean released = false;
private boolean isActivatorVisitedAtRender = false;
// request data
private ContextMode contextMode = null;
private String activatorComponentId = null;
private String behaviorEvent = null;
// computed properties
private Collection executeIds = null;
private Collection renderIds = null;
private Boolean renderAll = null;
// activator component data
private Collection componentRenderIds = null;
private String onbeforedomupdate;
private String oncomplete;
private Object responseData;
private boolean limitRender = false;
private Map responseComponentDataMap = Maps.newHashMap();
private StringBuilder beforedomupdateHandler = new StringBuilder();
private StringBuilder completeHandler = new StringBuilder();
// current visit mode setup during #processPartial method
private Stack visitMode = new Stack<>();
public ExtendedPartialViewContext(PartialViewContext wrappedViewContext, FacesContext facesContext) {
this.wrappedViewContext = wrappedViewContext;
this.facesContext = facesContext;
setInstance(facesContext, this);
}
/**
* The partial view processing mode
*/
private enum ContextMode {
/**
* Use wrapped {@link PartialViewContext} implementation for partial view processing.
*/
WRAPPED,
/**
* Use RichFaces-specific partial view processing processing via {@link ExtendedPartialViewContext}
*/
EXTENDED
}
/*
* (non-Javadoc)
*
* @see javax.faces.context.PartialViewContextWrapper#getWrapped()
*/
@Override
public PartialViewContext getWrapped() {
return wrappedViewContext;
}
/**
* This method is present in the JSF 2.2 PartialViewContextWrapper, but not in the JSF 2.1. Implementing it here so we are still compatible with JSF 2.1.
*/
@Override
public void setPartialRequest(boolean isPartialRequest) {
getWrapped().setPartialRequest(isPartialRequest);
}
/**
* Returns {@link FacesContext} for current partial view processing context
*/
protected FacesContext getFacesContext() {
return facesContext;
}
/**
* Return current {@link ExtendedPartialViewContext} instance for given {@link FacesContext}
*/
public static ExtendedPartialViewContext getInstance(FacesContext facesContext) {
return (ExtendedPartialViewContext) facesContext.getAttributes().get(ATTRIBUTE_NAME);
}
/**
* Setups given {@link ExtendedPartialViewContext} instance to context
* @param facesContext
* @param instance
*/
private static void setInstance(FacesContext facesContext, ExtendedPartialViewContext instance) {
facesContext.getAttributes().put(ATTRIBUTE_NAME, instance);
}
/**
*
* This method detects in which phase we are and setups {@link ExtendedVisitContextMode} attribute.
*
*
*
* Then it delegates to wrapped implementation of {@link PartialViewContext#processPartial(PhaseId)}.
*
*
*
* The {@link ExtendedVisitContextMode} attribute is used by {@link ExtendedVisitContextFactory} to create either
* {@link ExtendedExecuteVisitContext} or {@link ExtendedRenderVisitContext}.
*
*/
@Override
public void processPartial(PhaseId phaseId) {
initializeContext();
try {
if (isProcessedExecutePhase(phaseId)) {
setVisitMode(ExtendedVisitContextMode.EXECUTE);
} else {
setVisitMode(ExtendedVisitContextMode.RENDER);
}
wrappedViewContext.processPartial(phaseId);
} finally {
resetVisitMode();
}
}
private boolean isProcessedExecutePhase(PhaseId phaseId) {
return phaseId == PhaseId.APPLY_REQUEST_VALUES || phaseId == PhaseId.PROCESS_VALIDATIONS
|| phaseId == PhaseId.UPDATE_MODEL_VALUES;
}
/*
* (non-Javadoc)
*
* @see javax.faces.context.PartialViewContextWrapper#getExecuteIds()
*/
@Override
public Collection getExecuteIds() {
assertNotReleased();
if (detectContextMode() == ContextMode.EXTENDED) {
if (executeIds == null) {
executeIds = new LinkedHashSet<>();
visitActivatorAtExecute();
}
return executeIds;
} else {
return wrappedViewContext.getExecuteIds();
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.context.PartialViewContextWrapper#getRenderIds()
*/
@Override
public Collection getRenderIds() {
assertNotReleased();
if (detectContextMode() == ContextMode.EXTENDED) {
PhaseId currenPhaseId = facesContext.getCurrentPhaseId();
if (renderIds == null) {
renderIds = new LinkedHashSet<>();
}
if (currenPhaseId == PhaseId.RENDER_RESPONSE) {
visitActivatorAtRender();
}
return renderIds;
} else {
return wrappedViewContext.getRenderIds();
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.context.PartialViewContextWrapper#isExecuteAll()
*/
@Override
public boolean isExecuteAll() {
assertNotReleased();
if (detectContextMode() == ContextMode.EXTENDED) {
return getExecuteIds().contains(ALL);
} else {
return wrappedViewContext.isExecuteAll();
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.context.PartialViewContextWrapper#isRenderAll()
*/
@Override
public boolean isRenderAll() {
assertNotReleased();
if (detectContextMode() == ContextMode.EXTENDED) {
if (renderAll == null) {
setRenderAll(detectRenderAll());
}
return renderAll.booleanValue();
} else {
return wrappedViewContext.isRenderAll();
}
}
/**
* Detects whether current context's state indicates render=@all
*/
private boolean detectRenderAll() {
// RF-13740, MyFaces doesn't call for renderIds in advance
if (renderIds == null) {
renderIds = new LinkedHashSet<>();
PhaseId currentPhaseId = facesContext.getCurrentPhaseId();
// NEW: Do this only in the RENDER_RESPONSE Phase
// to fix #25
// https://github.com/albfernandez/richfaces/issues/25
if (currentPhaseId == PhaseId.RENDER_RESPONSE) {
visitActivatorAtRender();
}
}
return Boolean.TRUE.equals(renderAll) || renderIds.contains(ALL);
}
/*
* (non-Javadoc)
*
* @see javax.faces.context.PartialViewContextWrapper#setRenderAll(boolean)
*/
@Override
public void setRenderAll(final boolean renderAll) {
assertNotReleased();
this.renderAll = renderAll;
visitPatentContexts(new Function() {
public Void apply(PartialViewContext pvc) {
if (pvc != ExtendedPartialViewContext.this) {
pvc.setRenderAll(renderAll);
}
return null;
}
});
}
/**
* Returns user-provided data as an extension for partial-response response element
*/
public Object getResponseData() {
return responseData;
}
/**
* Returns data provided by components as an extension for partial-response response element
*/
public Map getResponseComponentDataMap() {
return responseComponentDataMap;
}
/**
* Sets user-provided data as an extension for partial-response
*/
public void setResponseData(Object responseData) {
this.responseData = responseData;
}
/**
* Append a script to oncomplete handler
*/
public void appendOncomplete(Object handler) {
if (handler != null) {
completeHandler.append(handler.toString());
completeHandler.append(';');
}
}
/**
* Prepend an oncomplete handler with given script
*/
public void prependOncomplete(Object handler) {
if (handler != null) {
completeHandler.insert(0, ';');
completeHandler.insert(0, handler.toString());
}
}
/**
* Return oncomplete handler as an extension for partial-response
*/
public Object getOncomplete() {
return completeHandler.toString();
}
/**
* Append a script to onbeforedomupdate handler
*/
public void appendOnbeforedomupdate(Object handler) {
if (handler != null) {
beforedomupdateHandler.append(handler.toString());
beforedomupdateHandler.append(';');
}
}
/**
* Prepend an onbeforedomupdate handler with given script
*/
public void prependOnbeforedomupdate(Object handler) {
if (handler != null) {
beforedomupdateHandler.insert(0, handler.toString());
beforedomupdateHandler.insert(0, ';');
}
}
/**
* Return onbeforedomupdate handler as an extension for partial-response
*/
public Object getOnbeforedomupdate() {
return beforedomupdateHandler.toString();
}
/**
* Returns true if rendering in current context is limited to components listed in activator's component render attribute.
*/
public boolean isLimitRender() {
return limitRender;
}
/**
* Returns in which visit mode we currently operate in
*/
public ExtendedVisitContextMode getVisitMode() {
if (visitMode.isEmpty()) {
return null;
}
return visitMode.peek();
}
/**
* Set ups current visit mode to given value.
*
* Works as a stack because {@link #processPartial(javax.faces.event.PhaseId)} methods that sets this flag may nest.
*
* @see #resetVisitMode()
*/
private void setVisitMode(ExtendedVisitContextMode visitMode) {
this.visitMode.add(visitMode);
}
/**
* Resets current visit mode.
*
* Works as a stack because {@link #processPartial(javax.faces.event.PhaseId)} methods that sets this flag may nest.
* Partial processing needs to {@link #resetVisitMode()} before returning.
*/
private void resetVisitMode() {
this.visitMode.pop();
}
/**
* We are wrapping {@link PartialResponseWriter} obtained from wrapped implementation into {@link ExtensionWritingPartialResponseWriter}.
*
* The wrapper makes sure the RichFaces-specific extensions are written into partial-response before the document is ended.
*/
@Override
public PartialResponseWriter getPartialResponseWriter() {
assertNotReleased();
if (partialResponseWriter == null) {
partialResponseWriter = new ExtensionWritingPartialResponseWriter(wrappedViewContext.getPartialResponseWriter());
}
return partialResponseWriter;
}
/**
* Makes sure the RichFaces-specific extensions are written into partial-response before the document is ended.
*/
private class ExtensionWritingPartialResponseWriter extends PartialResponseWriterWrapper {
public ExtensionWritingPartialResponseWriter(PartialResponseWriter wrapped) {
super(wrapped);
}
/**
* Render RichFaces-specific extensions and then {@link #endDocument()} finally.
*/
@Override
public void endDocument() throws IOException {
try {
FacesContext facesContext = FacesContext.getCurrentInstance();
UIViewRoot viewRoot = facesContext.getViewRoot();
addJavaScriptServicePageScripts(facesContext);
renderExtensions(facesContext, viewRoot);
} finally {
super.endDocument();
}
}
}
/**
* Visits activator component to collect attributes needed for execute phase
*/
private void visitActivatorAtExecute() {
if (detectContextMode() == ContextMode.EXTENDED) {
ActivatorComponentExecuteCallback callback = new ActivatorComponentExecuteCallback(getFacesContext(), behaviorEvent);
if (visitActivatorComponent(activatorComponentId, callback, EnumSet.of(VisitHint.SKIP_UNRENDERED))) {
executeIds.addAll(callback.getExecuteIds());
setupRenderCallbackData(callback);
if (!executeIds.contains(ALL)) {
addImplicitExecuteIds(executeIds);
}
}
}
}
/**
* Copies the data collected from activator component processing to to this context
*/
private void setupRenderCallbackData(ActivatorComponentRenderCallback callback) {
componentRenderIds = callback.getRenderIds();
onbeforedomupdate = callback.getOnbeforedomupdate();
oncomplete = callback.getOncomplete();
responseData = callback.getData();
limitRender = callback.isLimitRender();
}
/**
* Visits activator component to collect attributes needed for render phase
*/
private void visitActivatorAtRender() {
if (detectContextMode() == ContextMode.EXTENDED && !isActivatorVisitedAtRender) {
ActivatorComponentRenderCallback callback = new ActivatorComponentRenderCallback(getFacesContext(), behaviorEvent);
if (visitActivatorComponent(activatorComponentId, callback, EnumSet.of(VisitHint.SKIP_UNRENDERED))) {
setupRenderCallbackData(callback);
} else {
// TODO - the same as for "execute"
}
// take collection value stored during execute
if (componentRenderIds != null) {
renderIds.addAll(componentRenderIds);
}
if (!isRenderAll()) {
addImplicitRenderIds(renderIds);
appendOnbeforedomupdate(onbeforedomupdate);
appendOncomplete(oncomplete);
setResponseData(responseData);
}
isActivatorVisitedAtRender = true;
}
}
/*
* (non-Javadoc)
*
* @see org.richfaces.context.ExtendedPartialViewContext#release()
*/
@Override
public void release() {
assertNotReleased();
super.release();
if (facesContext != null && !facesContext.isReleased()) {
setInstance(facesContext, null);
}
facesContext = null;
released = true;
wrappedViewContext.release();
wrappedViewContext = null;
renderAll = null;
executeIds = null;
renderIds = null;
limitRender = false;
activatorComponentId = null;
behaviorEvent = null;
contextMode = null;
}
/**
* Adding implicitly executed areas to the list of component that should be executed.
*
* This implementation handles just {@link UIViewRoot#METADATA_FACET_NAME} execution.
*/
protected void addImplicitExecuteIds(Collection executeIds) {
if (!executeIds.isEmpty()) {
UIViewRoot root = getFacesContext().getViewRoot();
if (root.getFacetCount() > 0) {
if (root.getFacet(UIViewRoot.METADATA_FACET_NAME) != null) {
executeIds.add(UIViewRoot.METADATA_FACET_NAME);
}
}
}
}
/**
* Adding implicitly renderer areas to the list of component that should be rendered.
*
*
*/
protected void addImplicitRenderIds(Collection renderIds) {
if (!limitRender) {
final FacesContext facesContext = getFacesContext();
Collection ajaxOutputs = AjaxOutputTracker.getAjaxOutputs(facesContext, facesContext.getViewRoot());
for (UIComponent component : ajaxOutputs) {
if (component instanceof AjaxOutput && ((AjaxOutput)component).isAjaxRendered()) {
renderIds.add(component.getClientId(facesContext));
}
}
}
}
/**
* Add scripts collected by {@link JavaScriptService} as a partial response extension.
*/
protected void addJavaScriptServicePageScripts(FacesContext context) {
ScriptsHolder scriptsHolder = ServiceTracker.getService(JavaScriptService.class).getScriptsHolder(context);
StringBuilder scripts = new StringBuilder();
for (Object script : scriptsHolder.getScripts()) {
scripts.append(ScriptUtils.toScript(script));
scripts.append(";");
}
for (Object script : scriptsHolder.getPageReadyScripts()) {
scripts.append(ScriptUtils.toScript(script));
scripts.append(";");
}
if (scripts.length() > 0) {
scripts.append("RichFaces.javascriptServiceComplete();");
prependOncomplete(scripts.toString());
}
}
/**
* Render RichFaces specific extensions for partial-update such as:
*
*
* - beforedumpdate
* - complete
* - render
* - data
* - componentData
*
*/
protected void renderExtensions(FacesContext context, UIComponent component) throws IOException {
Map attributes = Collections.singletonMap(HtmlConstants.ID_ATTRIBUTE, context.getExternalContext()
.encodeNamespace(EXTENSION_ID));
PartialResponseWriter writer = context.getPartialViewContext().getPartialResponseWriter();
boolean[] writingState = new boolean[] { false };
Object onbeforedomupdate = this.getOnbeforedomupdate();
if (onbeforedomupdate != null) {
String string = onbeforedomupdate.toString();
if (string.length() != 0) {
startExtensionElementIfNecessary(writer, attributes, writingState);
writer.startElement(BEFOREDOMUPDATE_ELEMENT_NAME, component);
writer.writeText(onbeforedomupdate, null);
writer.endElement(BEFOREDOMUPDATE_ELEMENT_NAME);
}
}
Object oncomplete = this.getOncomplete();
if (oncomplete != null) {
String string = oncomplete.toString();
if (string.length() != 0) {
startExtensionElementIfNecessary(writer, attributes, writingState);
writer.startElement(COMPLETE_ELEMENT_NAME, component);
writer.writeText(oncomplete, null);
writer.endElement(COMPLETE_ELEMENT_NAME);
}
}
if (!this.getRenderIds().isEmpty()) {
String renderIds = SPACE_JOINER.join(this.getRenderIds());
startExtensionElementIfNecessary(writer, attributes, writingState);
writer.startElement(RENDER_ELEMENT_NAME, component);
writer.writeText(renderIds, null);
writer.endElement(RENDER_ELEMENT_NAME);
}
Object responseData = this.getResponseData();
if (responseData != null) {
startExtensionElementIfNecessary(writer, attributes, writingState);
writer.startElement(DATA_ELEMENT_NAME, component);
AjaxDataSerializer serializer = ServiceTracker.getService(context, AjaxDataSerializer.class);
writer.writeText(serializer.asString(responseData), null);
writer.endElement(DATA_ELEMENT_NAME);
}
Map responseComponentDataMap = this.getResponseComponentDataMap();
if (responseComponentDataMap != null && !responseComponentDataMap.isEmpty()) {
startExtensionElementIfNecessary(writer, attributes, writingState);
writer.startElement(COMPONENT_DATA_ELEMENT_NAME, component);
AjaxDataSerializer serializer = ServiceTracker.getService(context, AjaxDataSerializer.class);
writer.writeText(serializer.asString(responseComponentDataMap), null);
writer.endElement(COMPONENT_DATA_ELEMENT_NAME);
}
endExtensionElementIfNecessary(writer, writingState);
}
/**
* Asserts that this context was not released yet
*/
private void assertNotReleased() {
if (released) {
throw new IllegalStateException("PartialViewContext already released!");
}
}
/**
* Visits activator component in order to collect its data
*/
private boolean visitActivatorComponent(String componentActivatorId, VisitCallback visitCallback, Set visitHints) {
final FacesContext facesContext = getFacesContext();
try {
Set idsToVisit = Collections.singleton(componentActivatorId);
setVisitMode(ExtendedVisitContextMode.EXECUTE);
VisitContextFactory visitContextFactory = (VisitContextFactory) FactoryFinder
.getFactory(javax.faces.FactoryFinder.VISIT_CONTEXT_FACTORY);
VisitContext visitContext = visitContextFactory.getVisitContext(facesContext, idsToVisit, visitHints);
return facesContext.getViewRoot().visitTree(visitContext, visitCallback);
} finally {
resetVisitMode();
}
}
/**
* Detects in what context mode should be this partial view request processed
*/
protected ContextMode detectContextMode() {
initializeContext();
return contextMode;
}
/**
* Initializes context:
*
*
* - {@link #contextMode}
* - {@link #activatorComponentId}
* - {@link #behaviorEvent}
*
*/
protected void initializeContext() {
if (contextMode == null) {
Map requestParameterMap = getFacesContext().getExternalContext().getRequestParameterMap();
activatorComponentId = requestParameterMap.get(AJAX_COMPONENT_ID_PARAMETER);
if (activatorComponentId != null) {
contextMode = ContextMode.EXTENDED;
behaviorEvent = requestParameterMap.get(BEHAVIOR_EVENT_PARAMETER);
} else {
contextMode = ContextMode.WRAPPED;
}
}
}
private static void startExtensionElementIfNecessary(PartialResponseWriter partialResponseWriter,
Map attributes, boolean[] writingState) throws IOException {
if (!writingState[0]) {
writingState[0] = true;
partialResponseWriter.startExtension(attributes);
}
}
private static void endExtensionElementIfNecessary(PartialResponseWriter partialResponseWriter, boolean[] writingState)
throws IOException {
if (writingState[0]) {
writingState[0] = false;
partialResponseWriter.endExtension();
}
}
/**
* All the parent wrappers of this context will be traversed and given callback will be called upon them
*/
private void visitPatentContexts(Function function) {
PartialViewContext pvc = (PartialViewContextWrapper) this;
do {
pvc = ((PartialViewContextWrapper) pvc).getWrapped();
function.apply(pvc);
} while (pvc instanceof PartialViewContextWrapper);
}
}