// Path: src/main/java/org/expedientframework/workflowlite/core/utils/ThreadLocalSentry.java
// public class ThreadLocalSentry<T> implements Closeable
// {
//   public ThreadLocalSentry(final T initialValue)
//   {
//     this.threadLocal = new ThreadLocal<>();
//     this.threadLocal.set(initialValue);
//   }
// 
//   public ThreadLocalSentry<T> set(final T value)
//   {
//     this.threadLocal.set(value);
//     return this;
//   }
//   
//   public T get()
//   {
//     return this.threadLocal.get();
//   }
//   
//   @Override
//   public void close()
//   {
//     this.threadLocal.remove();
//   }
//   
//   // Private
//   private ThreadLocal<T> threadLocal;
// }


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.expedientframework.workflowlite.core.utils.ThreadLocalSentry;

    return this.doEvaluate(expression, EXPRESSION_EVALUATION_CONTEXT.get());
  }

  @Override
  public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException
  {
    this.existingExpressionResolver = beanFactory.getBeanExpressionResolver();
    beanFactory.setBeanExpressionResolver(this);
  }

  @Override
  public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
  {
    this.applicationContext = applicationContext;
  }
  
  private Object doEvaluate(final String expression, final EvaluationContext evaluationContext)
  {
    // TODO: Ajey - Cache the expression object
    final Expression expressionObject = this.expressionParser.parseExpression(expression, PARSER_CONTEXT);
    
    return expressionObject.getValue(evaluationContext);
  }

  // Private
  private ApplicationContext applicationContext;
  private BeanExpressionResolver existingExpressionResolver;
  private final SpelExpressionParser expressionParser = new SpelExpressionParser();
  
// Path: src/main/java/org/expedientframework/workflowlite/core/utils/ThreadLocalSentry.java
// public class ThreadLocalSentry<T> implements Closeable
// {
//   public ThreadLocalSentry(final T initialValue)
//   {
//     this.threadLocal = new ThreadLocal<>();
//     this.threadLocal.set(initialValue);
//   }
// 
//   public ThreadLocalSentry<T> set(final T value)
//   {
//     this.threadLocal.set(value);
//     return this;
//   }
//   
//   public T get()
//   {
//     return this.threadLocal.get();
//   }
//   
//   @Override
//   public void close()
//   {
//     this.threadLocal.remove();
//   }
//   
//   // Private
//   private ThreadLocal<T> threadLocal;
// }


// Path: src/main/java/org/expedientframework/workflowlite/core/BeanInstantiator.java
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.expedientframework.workflowlite.core.utils.ThreadLocalSentry;


    return this.doEvaluate(expression, EXPRESSION_EVALUATION_CONTEXT.get());
  }

  @Override
  public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException
  {
    this.existingExpressionResolver = beanFactory.getBeanExpressionResolver();
    beanFactory.setBeanExpressionResolver(this);
  }

  @Override
  public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
  {
    this.applicationContext = applicationContext;
  }
  
  private Object doEvaluate(final String expression, final EvaluationContext evaluationContext)
  {
    // TODO: Ajey - Cache the expression object
    final Expression expressionObject = this.expressionParser.parseExpression(expression, PARSER_CONTEXT);
    
    return expressionObject.getValue(evaluationContext);
  }

  // Private
  private ApplicationContext applicationContext;
  private BeanExpressionResolver existingExpressionResolver;
  private final SpelExpressionParser expressionParser = new SpelExpressionParser();
  
  private static final ThreadLocalSentry<EvaluationContext> EXPRESSION_EVALUATION_CONTEXT = new ThreadLocalSentry<>(null);