/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.ajax4jsf.component;
import java.io.IOException;
import java.util.NoSuchElementException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import org.ajax4jsf.Messages;
import org.ajax4jsf.application.AjaxSingleException;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.AjaxContextImpl;
import org.ajax4jsf.context.InvokerCallback;
import org.ajax4jsf.context.ViewIdHolder;
import org.ajax4jsf.event.AjaxListener;
import org.ajax4jsf.event.EventsQueue;
import org.ajax4jsf.renderkit.AjaxContainerRenderer;
/**
* Custom ViewRoot for support render parts of tree for Ajax requests. Main
* difference from default ViewRoot - store events queue and unique id counter
* in request-scope variables. In common realisation, store request-scope
* variables in component produce errors in case of concurrent requests to same
* view.
*
* @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.4 $ $Date: 2007/02/28 17:01:01 $
*
*/
public class AjaxViewRoot extends UIViewRoot implements AjaxContainer {
public static final String ROOT_ID = "_viewRoot";
private AjaxRegionBrige _brige;
/**
*
*/
public AjaxViewRoot() {
super();
super.setId(ROOT_ID);
_brige = new AjaxRegionBrige(this);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIComponentBase#getId()
*/
// public String getId() {
// return ROOT_ID;
// }
public String getRendererType() {
return (COMPONENT_FAMILY);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#broadcast(javax.faces.event.FacesEvent)
*/
public void broadcast(FacesEvent event) throws AbortProcessingException {
super.broadcast(event);
getBrige().broadcast(event);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#getAjaxListener()
*/
public MethodBinding getAjaxListener() {
return getBrige().getAjaxListener();
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#isImmediate()
*/
public boolean isImmediate() {
return getBrige().isImmediate();
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#isSubmitted()
*/
public boolean isSubmitted() {
return getBrige().isSubmitted();
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIComponent#queueEvent(javax.faces.event.FacesEvent)
*/
public void queueEvent(FacesEvent event) {
if (event == null)
throw new NullPointerException(Messages
.getMessage(Messages.NULL_EVENT_SUBMITTED_ERROR));
if (event.getPhaseId().compareTo(PhaseId.RENDER_RESPONSE) == 0) {
// HACK - Special case - Ajax Events to RenderResponse phase
// queue.
// in future, with JSF 1.2 it must be done by invokeOnComponent
// method.
// In 1.1 , events to RENDER_RESPONSE phase not used.
getAjaxEventsQueue(getFacesContext()).add(event);
} else {
getEventsQueue(getFacesContext(), event.getPhaseId()).add(event);
}
}
/**
* Broadcast events for specified Phase
*
* @param context
* @param phaseId -
* phase, for wich events must be processed.
*/
void broadcastEvents(FacesContext context, PhaseId phaseId) {
EventsQueue[] events = getEvents(context);
EventsQueue anyPhaseEvents = events[PhaseId.ANY_PHASE.getOrdinal()];
EventsQueue phaseEvents = events[phaseId.getOrdinal()];
if (phaseEvents.isEmpty() && anyPhaseEvents.isEmpty())
return;
// FacesEvent event = null;
boolean haveAnyPhaseEvents = !anyPhaseEvents.isEmpty();
boolean havePhaseEvents = !phaseEvents.isEmpty();
do {
// ANY_PHASE first
processEvents(anyPhaseEvents, haveAnyPhaseEvents);
processEvents(phaseEvents, havePhaseEvents);
// Events can queued in other events processing
haveAnyPhaseEvents = !anyPhaseEvents.isEmpty();
havePhaseEvents = !phaseEvents.isEmpty();
} while (haveAnyPhaseEvents || havePhaseEvents);
if (context.getRenderResponse() || context.getResponseComplete()) {
clearEvents(context);
}
}
/**
* @param phaseEventsQueue
* @param havePhaseEvents
*/
public void processEvents(EventsQueue phaseEventsQueue, boolean havePhaseEvents) {
FacesEvent event;
while (havePhaseEvents) {
try {
event = (FacesEvent) phaseEventsQueue.remove();
UIComponent source = event.getComponent();
try {
source.broadcast(event);
} catch (AbortProcessingException e) {
// abort event processing
// Page 3-30 of JSF 1.1 spec: "Throw an
// AbortProcessingException, to tell the JSF
// implementation
// that no further broadcast of this event, or any
// further
// events, should take place."
// clearEvents();
// return;
}
} catch (NoSuchElementException e) {
havePhaseEvents = false;
}
}
}
public void broadcastAjaxEvents(FacesContext context) {
EventsQueue queue = getAjaxEventsQueue(context);
processEvents(queue, !queue.isEmpty());
}
private EventsQueue[] events;
private EventsQueue ajaxEvents = new EventsQueue();
/**
* Use FIFO buffers for hold Faces Events. Hold this buffers in Request
* scope parameters for support any concurrent requests for same
* component tree ( since RI hold component tree in session ).
*
* @param context
* @param phase
* @return
*/
protected EventsQueue getEventsQueue(FacesContext context, PhaseId phase) {
return getEvents(context)[phase.getOrdinal()];
}
/**
* @return
*/
protected EventsQueue[] getEvents(FacesContext context) {
if (events == null) {
clearEvents(context);
}
return events;
}
/**
* Special Fifo Buffer for ajax events to Render Responce phase.
*
* @param context
* @return
*/
protected EventsQueue getAjaxEventsQueue(FacesContext context) {
return ajaxEvents;
}
public void clearEvents(FacesContext context) {
int len;
events = new EventsQueue[len = PhaseId.VALUES.size()];
for (int i = 0; i < len; i++) {
events[i] = new EventsQueue();
}
}
private InvokerCallback _decodeInvoker = new InvokerCallback() {
public void invoke(FacesContext context, UIComponent component) {
component.processDecodes(context);
}
public void invokeRoot(FacesContext context) {
AjaxViewRoot.super.processDecodes(context);
}
};
private UIComponent singleComponent = null;
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#processDecodes(javax.faces.context.FacesContext)
*/
public void processDecodes(FacesContext context) {
if (context == null)
throw new NullPointerException("context");
singleComponent = null;
AjaxContextImpl.invokeOnRegionOrRoot(this, context, _decodeInvoker);
try {
broadcastEvents(context, PhaseId.APPLY_REQUEST_VALUES);
} catch (AjaxSingleException e) {
singleComponent = e.getComponent();
}
}
private InvokerCallback _updatesInvoker = new InvokerCallback() {
public void invoke(FacesContext context, UIComponent component) {
component.processUpdates(context);
}
public void invokeRoot(FacesContext context) {
AjaxViewRoot.super.processUpdates(context);
}
};
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#processUpdates(javax.faces.context.FacesContext)
*/
public void processUpdates(FacesContext context) {
if (context == null)
throw new NullPointerException("context");
if (null == singleComponent) {
AjaxContextImpl.invokeOnRegionOrRoot(this, context, _updatesInvoker);
} else {
singleComponent.processUpdates(context);
}
broadcastEvents(context, PhaseId.UPDATE_MODEL_VALUES);
}
private InvokerCallback _validatorsInvoker = new InvokerCallback() {
public void invoke(FacesContext context, UIComponent component) {
component.processValidators(context);
}
public void invokeRoot(FacesContext context) {
AjaxViewRoot.super.processValidators(context);
}
};
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#processValidators(javax.faces.context.FacesContext)
*/
public void processValidators(FacesContext context) {
if (context == null)
throw new NullPointerException("context");
if (null == singleComponent) {
AjaxContextImpl.invokeOnRegionOrRoot(this, context, _validatorsInvoker);
} else {
singleComponent.processValidators(context);
}
broadcastEvents(context, PhaseId.PROCESS_VALIDATIONS);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIViewRoot#processApplication(javax.faces.context.FacesContext)
*/
public void processApplication(FacesContext context) {
if (context == null)
throw new NullPointerException("context");
// UIComponent component = getSubmittedRegion(context);
// TODO - process JSF 1.2 listeners
broadcastEvents(context, PhaseId.INVOKE_APPLICATION);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIViewRoot#encodeBegin(javax.faces.context.FacesContext)
*/
// public void encodeBegin(FacesContext context) throws IOException {
// UIComponent submittedComponent = getSubmittedRegion(context);
// if(null == submittedComponent){
// super.encodeBegin(context);
// } else {
// submittedComponent.encodeBegin(context);
// }
// }
private InvokerCallback _ajaxInvoker = new InvokerCallback() {
public void invoke(FacesContext context, UIComponent component) {
try {
if (component instanceof AjaxContainer) {
AjaxContainer ajax = (AjaxContainer) component;
ajax.encodeAjax(context);
} else {
// Container not found, use Root for encode.
encodeAjax(context);
}
} catch (IOException e) {
throw new FacesException(e);
}
}
public void invokeRoot(FacesContext context) {
try {
encodeAjax(context);
} catch (IOException e) {
throw new FacesException(e);
}
}
};
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIComponentBase#encodeChildren(javax.faces.context.FacesContext)
*/
public void encodeChildren(FacesContext context) throws IOException {
if (!isHavePage()
&& AjaxContext.getCurrentInstance(context).isAjaxRequest(
context)) {
AjaxContextImpl.invokeOnRegionOrRoot(this, context, _ajaxInvoker);
} else {
super.encodeChildren(context);
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
*/
// public void encodeEnd(FacesContext context) throws IOException {
// UIComponent submittedComponent = getSubmittedRegion(context);
// if (null == submittedComponent) {
// super.encodeEnd(context);
// } else {
// submittedComponent.encodeEnd(context);
// }
// }
public void restoreState(FacesContext context, Object state) {
Object[] mystate = (Object[]) state;
super.restoreState(context, mystate[0]);
getBrige().restoreState(context, mystate[1]);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#saveState(javax.faces.context.FacesContext)
*/
public Object saveState(FacesContext context) {
Object[] state = new Object[2];
state[0] = super.saveState(context);
state[1] = getBrige().saveState(context);
return state;
}
public String getViewId() {
ViewIdHolder viewIdHolder = AjaxContext.getCurrentInstance()
.getViewIdHolder();
String viewId;
if (null != viewIdHolder) {
viewId = viewIdHolder.getViewId();
} else {
viewId = super.getViewId();
}
return viewId;
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setAjaxListener(javax.faces.el.MethodBinding)
*/
public void setAjaxListener(MethodBinding ajaxListener) {
getBrige().setAjaxListener(ajaxListener);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setImmediate(boolean)
*/
public void setImmediate(boolean immediate) {
getBrige().setImmediate(immediate);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setSubmitted(boolean)
*/
public void setSubmitted(boolean submitted) {
getBrige().setSubmitted(submitted);
}
public void addAjaxListener(AjaxListener listener) {
addFacesListener(listener);
}
public AjaxListener[] getAjaxListeners() {
return (AjaxListener[]) getFacesListeners(AjaxListener.class);
}
public void removeAjaxListener(AjaxListener listener) {
removeFacesListener(listener);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#isSelfRendered()
*/
public boolean isSelfRendered() {
return false;// _brige.isSelfRendered();
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxViewBrige#setSelfRendered(boolean)
*/
public void setSelfRendered(boolean selfRendered) {
// _brige.setSelfRendered(selfRendered);
}
/**
* Check for AjaxContainer component in childreh. This is workaround for
* MyFaces JSP tag implementation, not called any encode... methods in
* <f:view> tag. If only one AjaxContainer present as children for
* viewRoot, decoding methods delegated to them.
*
* @return Returns the havePage.
*/
public boolean isHavePage() {
return (getChildCount() == 1 && getChildren().get(0) instanceof AjaxContainer);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIComponentBase#getRendersChildren()
*/
public boolean getRendersChildren() {
FacesContext context = FacesContext.getCurrentInstance();
// For non Ajax request, view root not render children
if (!AjaxContext.getCurrentInstance(context).isAjaxRequest(context)) {
return false;
}
// Also, if have page component as clild - delegate rendering of
// children to it.
if (isHavePage()) {
return false;
}
// Ajax Request. Control all output.
return true;
}
public boolean isRenderRegionOnly() {
// for viewroot it not applicable.
return false;
}
public void setRenderRegionOnly(boolean reRenderPage) {
// TODO Auto-generated method stub
}
public void encodeAjax(FacesContext context) throws IOException {
String rendererType = getRendererType();
if (rendererType != null) {
((AjaxContainerRenderer) getRenderer(context)).encodeAjax(context,
this);
}
}
/**
* @return the brige
*/
protected AjaxRegionBrige getBrige() {
return _brige;
}
}