package org.jboss.loom.utils.el;

import java.lang.reflect.Method;
import javax.el.CompositeELResolver;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.ExpressionFactory;
import javax.el.FunctionMapper;
import javax.el.ListELResolver;
import javax.el.MapELResolver;
import javax.el.ValueExpression;
import javax.el.VariableMapper;

/**
 * JUEL: http://juel.sourceforge.net/guide/start.html
 */
public class JuelCustomResolverEvaluator implements IExprLangEvaluator {
    private static final ExpressionFactory JUEL_FACTORY = new de.odysseus.el.ExpressionFactoryImpl();
    private final IVariablesProvider varProvider;


    public JuelCustomResolverEvaluator( IVariablesProvider variableProvider ) {
        this.varProvider = variableProvider;
    }


    // TODO: Move ELContext anonymous innter class to normal outer; don't create it for every call.
    @Override
    public String evaluateEL( String expr ) {
        
        // CompositeELResolver allows to resolve from multiple sources.
        final CompositeELResolver resolver = new CompositeELResolver();
        
        // Here I want to use Map to be able to add some values, e.g. from user input.
        resolver.add( new MapELResolver() );
        resolver.add( new ListELResolver() );
        
        // BeanELDefaultStringResolver is my implementation which returns "" if it can't find given variable
        // (instead of throwing an exception).
        resolver.add( new BeanELDefaultStringResolver( "" ) );
        
        //de.odysseus.el.util.SimpleContext context = new de.odysseus.el.util.SimpleContext();
        ELContext elCtx = new ELContext() {
            @Override public ELResolver getELResolver() { return resolver; }

            @Override public FunctionMapper getFunctionMapper() { return THROW_MAPPER; }

            @Override public VariableMapper getVariableMapper() { 
                return new ProvidedVariableJuelMapper( JuelCustomResolverEvaluator.this.varProvider );
            }
        };
        
        ValueExpression valueExpr = JUEL_FACTORY.createValueExpression( elCtx, expr, String.class );
        try {
            return (String) valueExpr.getValue( elCtx );
        } catch( javax.el.PropertyNotFoundException ex ) {
            throw new IllegalArgumentException( "Can't eval '" + expr + "':\n    " + ex.getMessage(), ex );
        }
    }
    
    
    /**
     *  Throws UnsupportedOperationException for any call.
     */
    static final FunctionMapper THROW_MAPPER = 
            new FunctionMapper() {
                @Override public Method resolveFunction( String prefix, String localName ) {
                    throw new UnsupportedOperationException( "No functions supported." );
                }
            };
    
}// class

@@ -926,24 +926,132 @@
 ng expr ) %7B%0A
+        if( expr == null )%0A            throw new IllegalArgumentException(%22The expression param is null.%22);%0A
         %0A   
@@ -1597,20 +1597,18 @@
 esolver(
- 
 %22%22
- 
 ) );%0A   
@@ -2116,32 +2116,50 @@
     %7D;%0A        %0A
+        try %7B%0A    
         ValueExp
@@ -2243,30 +2243,16 @@
 lass );%0A
-        try %7B%0A
         
@@ -2463,24 +2463,166 @@
 ge(), ex );%0A
+        %7D catch( Throwable ex ) %7B%0A            throw new IllegalArgumentException( %22Can't eval '%22 + expr + %22':%5Cn    %22 + ex.getMessage(), ex );%0A
         %7D%0A  

Fix NPE if property with @EL is null.