package org.jboss.seam.el;
import org.jboss.seam.util.JSF;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import javax.el.ELContext;
import javax.el.ELResolver;
import org.jboss.seam.Namespace;
import org.jboss.seam.contexts.Context;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Init;
/**
* Resolves Seam components and namespaces. Also
* allows the use of #{dataModel.size}, #{dataModel.empty},
* #{collection.size}, #{map.size}, #{map.values}, #{map.keySet},
* and #{map.entrySet}. Also allows #{sessionContext['name']}.
*
* @author Gavin King
*
*/
public class SeamELResolver extends ELResolver
{
@Override
public Class getCommonPropertyType(ELContext context, Object base)
{
return null;
}
@Override
public Iterator getFeatureDescriptors(ELContext context, Object base)
{
return null;
}
@Override
public Class getType(ELContext context, Object base, Object property)
{
return null;
}
@Override
public Object getValue(ELContext context, Object base, Object property)
{
if (base == null) {
return resolveBase(context, property);
} else if (base instanceof Namespace) {
return resolveInNamespace(context, (Namespace) base, property);
} else if (JSF.DATA_MODEL.isInstance(base)) {
return resolveInDataModel(context, base, property);
} else if (base instanceof Collection) {
return resolveInCollection(context, (Collection) base, property);
} else if (base instanceof Map) {
return resolveInMap(context, (Map) base, property);
} else if (base instanceof Context) {
return resolveInContextObject(context, (Context) base, property);
} else {
return null;
}
}
private Object resolveInContextObject(ELContext context, Context seamContext, Object property)
{
if (seamContext.isSet((String) property)) {
context.setPropertyResolved(true);
return seamContext.get((String) property);
} else {
return null;
}
}
private boolean containsKey(Map map, String key) {
try {
return map.containsKey(key);
} catch (UnsupportedOperationException e) {
// eat it
return false;
}
}
private Object resolveInMap(ELContext context, Map map, Object property) {
if ("size".equals(property) && !containsKey(map,"size")) {
context.setPropertyResolved(true);
return map.size();
} else if ("values".equals(property) && !containsKey(map,"values")) {
context.setPropertyResolved(true);
return map.values();
} else if ("keySet".equals(property) && !containsKey(map,"keySet")) {
context.setPropertyResolved(true);
return map.keySet();
} else if ("entrySet".equals(property) && !containsKey(map,"entrySet")) {
context.setPropertyResolved(true);
return map.entrySet();
} else {
return null;
}
}
private Object resolveInCollection(ELContext context, Collection collection, Object property)
{
if ("size".equals(property)) {
context.setPropertyResolved(true);
return collection.size();
} else {
return null;
}
}
private Object resolveInDataModel(ELContext context, Object base, Object property)
{
if ("size".equals(property)) {
context.setPropertyResolved(true);
return JSF.getRowCount(base);
} else if ("empty".equals(property)) {
context.setPropertyResolved(true);
return JSF.getRowCount(base) == 0;
} else {
return null;
}
}
private Object resolveBase(ELContext context, Object property)
{
if (!Contexts.isApplicationContextActive()) {
// if no Seam contexts, bypass straight through to JSF
return null;
}
String key = (String) property;
Init init = Init.instance();
// look for a component in the root namespace
Object result = init.getRootNamespace().getComponentInstance(key);
if (result != null) {
context.setPropertyResolved(true);
return result;
} else {
// look for a component in the imported namespaces
for (Namespace ns : init.getGlobalImports()) {
result = ns.getComponentInstance(key);
if (result != null) {
context.setPropertyResolved(true);
return result;
}
}
}
// look for a namespace
Namespace namespace = init.getRootNamespace().getChild(key);
if (namespace != null) {
context.setPropertyResolved(true);
}
return namespace;
}
private Object resolveInNamespace(ELContext context, Namespace namespace, Object property) {
Object result = namespace.get((String) property);
// JBSEAM-3077 if the result is null, it means that there is no component instance bound to this qualified name
context.setPropertyResolved(true);
return result;
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property)
{
return base != null
&& (JSF.DATA_MODEL.isInstance(base) || (base instanceof Collection) || (base instanceof Map));
}
@Override
public void setValue(ELContext context, Object base, Object property, Object value)
{
}
}