/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.contexts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.Events;
/**
* The page context allows you to store state during a request that
* renders a page, and access that state from any postback request
* that originates from that page. The state is destroyed at the
* end of the second request. During the RENDER_RESPONSE phase,
* the page context instance refers to the page that is about to
* be rendered. Prior to the INVOKE_APPLICATION phase, it refers
* to the page that was the source of the request. During the
* INVOKE_APPLICATION phase, set() and remove() manipulate the
* context of the page that is about to be rendered, while get()
* returns values from the page that was the source of the request.
*
* @author Gavin King
*/
public class PageContext implements Context
{
private static final String PAGE_CONTEXT_PREFIX = ScopeType.PAGE.getPrefix() + '$';
private Map<String, Object> previousPageMap;
private Map<String, Object> nextPageMap;
public PageContext()
{
previousPageMap = getOrCreateAttributeMap();
nextPageMap = new HashMap<String, Object>();
}
public ScopeType getType()
{
return ScopeType.PAGE;
}
private String getKey(String name)
{
return getPrefix() + name;
}
private String getPrefix()
{
return PAGE_CONTEXT_PREFIX;
}
public Object get(String name)
{
return getCurrentReadableMap().get( getKey(name) );
}
public boolean isSet(String name)
{
return getCurrentReadableMap().containsKey( getKey(name) );
}
private Map<String, Object> getCurrentReadableMap()
{
if ( !isInPhase() )
{
return Collections.EMPTY_MAP;
}
else
{
return isRenderResponsePhase() ?
nextPageMap : previousPageMap;
}
}
private Map<String, Object> getCurrentWritableMap()
{
return isBeforeInvokeApplicationPhase() ?
previousPageMap : nextPageMap;
}
public void set(String name, Object value)
{
if ( Events.exists() ) Events.instance().raiseEvent("org.jboss.seam.preSetVariable." + name);
getCurrentWritableMap().put( getKey(name), value );
if ( Events.exists() ) Events.instance().raiseEvent("org.jboss.seam.postSetVariable." + name);
}
public void remove(String name)
{
if ( Events.exists() ) Events.instance().raiseEvent("org.jboss.seam.preRemoveVariable." + name);
getCurrentWritableMap().remove( getKey(name) );
if ( Events.exists() ) Events.instance().raiseEvent("org.jboss.seam.postRemoveVariable." + name);
}
public String[] getNames()
{
Set<String> keys = getCurrentReadableMap().keySet();
List<String> names = new ArrayList<String>( keys.size() );
String prefix = getPrefix();
for (String key: keys)
{
if ( key.startsWith(prefix) )
{
names.add( key.substring( prefix.length() ) );
}
}
return names.toArray( new String[ names.size() ] );
}
@Override
public String toString()
{
return "PageContext";
}
public Object get(Class clazz)
{
return get( Component.getComponentName(clazz) );
}
/**
* Put the buffered context variables in the faces view root,
* at the beginning of the render phase.
*/
public void flush()
{
Map attributeMap = getOrCreateAttributeMap();
attributeMap.putAll(nextPageMap);
nextPageMap = attributeMap;
}
private static Map getOrCreateAttributeMap()
{
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext==null)
{
throw new IllegalStateException("no FacesContext bound to current thread");
}
UIViewRoot viewRoot = facesContext.getViewRoot();
return viewRoot==null ?
new HashMap() : viewRoot.getAttributes();
}
private static PhaseId getPhaseId()
{
PhaseId phaseId = FacesLifecycle.getPhaseId();
if (phaseId==null)
{
throw new IllegalStateException("No phase id bound to current thread (make sure you do not have two SeamPhaseListener instances installed)");
}
return phaseId;
}
private static boolean isInPhase()
{
return FacesLifecycle.getPhaseId()!=null;
}
private static boolean isBeforeInvokeApplicationPhase()
{
return getPhaseId().compareTo(PhaseId.INVOKE_APPLICATION) < 0;
}
private static boolean isRenderResponsePhase()
{
return getPhaseId().compareTo(PhaseId.INVOKE_APPLICATION) > 0;
}
}