//$Id: Expressions.java 14085 2011-04-22 09:36:42Z manaRH $
package org.jboss.seam.core;
import static org.jboss.seam.annotations.Install.BUILT_IN;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.el.EL;
import org.jboss.seam.el.SeamExpressionFactory;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
/**
* Factory for EL method and value expressions.
*
* This default implementation uses JBoss EL.
*
* @author Gavin King
*/
@Scope(ScopeType.APPLICATION)
@BypassInterceptors
@Install(precedence=BUILT_IN)
@Name("org.jboss.seam.core.expressions")
public class Expressions implements Serializable
{
private static final LogProvider log = Logging.getLogProvider(Expressions.class);
private static List<String> blacklist = new ArrayList<String>();
// loading blacklisted patterns of non-valid EL expressions
static
{
BufferedReader reader = null;
try
{
InputStream blacklistIS = ResourceLoader.instance().getResourceAsStream("blacklist.properties");
reader = new BufferedReader(new InputStreamReader(blacklistIS));
String line;
while ((line = reader.readLine()) != null)
{
blacklist.add(line);
}
}
catch (IOException e)
{
log.warn("Black list of non-valid EL expressions was not found!");
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException e) { }
}
}
}
/**
* Get the JBoss EL ExpressionFactory
*/
public ExpressionFactory getExpressionFactory()
{
return SeamExpressionFactory.INSTANCE;
}
/**
* Get an appropriate ELContext. If there is an active JSF request,
* use JSF's ELContext. Otherwise, use one that we create.
*/
public ELContext getELContext() {
return EL.createELContext();
}
/**
* Create a value expression.
*
* @param expression a JBoss EL value expression
*/
public ValueExpression<Object> createValueExpression(String expression)
{
return createValueExpression(expression, Object.class);
}
/**
* Create a method expression.
*
* @param expression a JBoss EL method expression
*/
public MethodExpression<Object> createMethodExpression(String expression)
{
return createMethodExpression(expression, Object.class);
}
/**
* Create a value expression.
*
* @param expression a JBoss EL value expression
* @param type the type of the value
*/
public <T> ValueExpression<T> createValueExpression(final String expression, final Class<T> type)
{
checkELExpression(expression);
return new ValueExpression<T>()
{
private javax.el.ValueExpression facesValueExpression;
private javax.el.ValueExpression seamValueExpression;
public javax.el.ValueExpression toUnifiedValueExpression()
{
if ( isFacesContextActive() )
{
if (facesValueExpression==null)
{
facesValueExpression = createExpression();
}
return facesValueExpression;
}
else
{
if (seamValueExpression==null)
{
seamValueExpression = createExpression();
}
return seamValueExpression;
}
}
private javax.el.ValueExpression createExpression()
{
return getExpressionFactory().createValueExpression( getELContext(), expression, type );
}
public T getValue()
{
return (T) toUnifiedValueExpression().getValue( getELContext() );
}
public void setValue(T value)
{
toUnifiedValueExpression().setValue( getELContext(), value );
}
public String getExpressionString()
{
return expression;
}
public Class<T> getType()
{
// QUESTION shouldn't we use the type provided in the constructor?
return (Class<T>) toUnifiedValueExpression().getType( getELContext() );
}
};
}
/**
* Create a method expression.
*
* @param expression a JBoss EL method expression
* @param type the method return type
* @param argTypes the method parameter types
*/
public <T> MethodExpression<T> createMethodExpression(final String expression, final Class<T> type, final Class... argTypes)
{
checkELExpression(expression);
return new MethodExpression<T>()
{
private javax.el.MethodExpression facesMethodExpression;
private javax.el.MethodExpression seamMethodExpression;
public javax.el.MethodExpression toUnifiedMethodExpression()
{
if ( isFacesContextActive() )
{
if (facesMethodExpression==null)
{
facesMethodExpression = createExpression();
}
return facesMethodExpression;
}
else
{
if (seamMethodExpression==null)
{
seamMethodExpression = createExpression();
}
return seamMethodExpression;
}
}
private javax.el.MethodExpression createExpression()
{
return getExpressionFactory().createMethodExpression( getELContext(), expression, type, argTypes );
}
public T invoke(Object... args)
{
return (T) toUnifiedMethodExpression().invoke( getELContext(), args );
}
public String getExpressionString()
{
return expression;
}
};
}
/**
* A value expression - an EL expression that evaluates to
* an attribute getter or get/set pair. This interface
* is just a genericized version of the Unified EL ValueExpression
* interface.
*
* @author Gavin King
*
* @param <T> the type of the value
*/
public static interface ValueExpression<T> extends Serializable
{
public T getValue();
public void setValue(T value);
public String getExpressionString();
public Class<T> getType();
/**
* @return the underlying Unified EL ValueExpression
*/
public javax.el.ValueExpression toUnifiedValueExpression();
}
/**
* A method expression - an EL expression that evaluates to
* a method. This interface is just a genericized version of
* the Unified EL ValueExpression interface.
*
* @author Gavin King
*
* @param <T> the method return type
*/
public static interface MethodExpression<T> extends Serializable
{
public T invoke(Object... args);
public String getExpressionString();
/**
* @return the underlying Unified EL MethodExpression
*/
public javax.el.MethodExpression toUnifiedMethodExpression();
}
protected boolean isFacesContextActive()
{
return false;
}
/*
* Gets the validator from the Component object (if this is a Seam
* component, we need to use the validator for the bean class, not
* the proxy class) or from a Model object (if it is not a Seam
* component, there isn't any proxy).
*
* @param instance the object to be validated
* @param componentName the name of the context variable, which might be a component name
* @return a ClassValidator object
*/
/*private static ClassValidator getValidator(Object instance, String componentName)
{
if (instance==null || componentName==null )
{
throw new IllegalArgumentException();
}
Component component = Component.forName(componentName);
return ( component==null ? Model.forClass( instance.getClass() ) : component ).getValidator();
}*/
public static Expressions instance()
{
if (!Contexts.isApplicationContextActive()) {
return new Expressions();
} else {
return (Expressions) Component.getInstance(Expressions.class, ScopeType.APPLICATION);
}
}
private static void checkELExpression(final String expression)
{
for (int index = 0; blacklist.size() > index; index++)
{
if ( expression.contains(blacklist.get(index)) ) {
throw new IllegalArgumentException("This EL expression is not allowed!");
}
}
// for any case blacklist is not provided this is definitely not permitted
if ( expression.contains(".getClass()") )
{
throw new IllegalArgumentException("This EL expression is not allowed!");
}
}
}