class ThymeleafEvaluationContext
            extends StandardEvaluationContext
            implements IThymeleafEvaluationContext {

    public static final String THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME = "thymeleaf::EvaluationContext";


    private static final ReflectivePropertyAccessor REFLECTIVE_PROPERTY_ACCESSOR_INSTANCE =
            new ThymeleafEvaluationContextACLPropertyAccessor();
    private static final MapAccessor MAP_ACCESSOR_INSTANCE = new MapAccessor();
    private static final TypeLocator TYPE_LOCATOR = new ThymeleafEvaluationContextACLTypeLocator();
    private static final List<MethodResolver> METHOD_RESOLVERS =
            Collections.singletonList(new ThymeleafEvaluationContextACLMethodResolver());


    private final ApplicationContext applicationContext;

    private IExpressionObjects expressionObjects = null;
    private boolean variableAccessRestricted = false;




    public ThymeleafEvaluationContext(final ApplicationContext applicationContext, final ConversionService conversionService) {

        super();

        Validate.notNull(applicationContext, "Application Context cannot be null");
        // ConversionService CAN be null

        this.applicationContext = applicationContext;
        this.setBeanResolver(new BeanFactoryResolver(applicationContext));
        if (conversionService != null) {
            this.setTypeConverter(new StandardTypeConverter(conversionService));
        }

        final List<PropertyAccessor> propertyAccessors = new ArrayList<>(5);
        propertyAccessors.add(SPELContextPropertyAccessor.INSTANCE);
        propertyAccessors.add(MAP_ACCESSOR_INSTANCE);
        propertyAccessors.add(REFLECTIVE_PROPERTY_ACCESSOR_INSTANCE);
        this.setPropertyAccessors(propertyAccessors);

        // We need to establish a custom type locator in order to forbid access to certain dangerous classes in expressions
        this.setTypeLocator(TYPE_LOCATOR);

        // We need to establish a custom method resolver in order to forbid calling methods on any of the blocked classes
        this.setMethodResolvers(METHOD_RESOLVERS);

    }


    public ApplicationContext getApplicationContext() {
        return this.applicationContext;
    }


    @Override
    public Object lookupVariable(final String name) {<FILL_FUNCTION_BODY>}




    public boolean isVariableAccessRestricted() {
        return this.variableAccessRestricted;
    }

    public void setVariableAccessRestricted(final boolean restricted) {
        this.variableAccessRestricted = restricted;
    }

    public IExpressionObjects getExpressionObjects() {
        return this.expressionObjects;
    }

    public void setExpressionObjects(final IExpressionObjects expressionObjects) {
        this.expressionObjects = expressionObjects;
    }



    static final class ThymeleafEvaluationContextACLTypeLocator implements TypeLocator {

        private final TypeLocator typeLocator;

        ThymeleafEvaluationContextACLTypeLocator() {
            this(new StandardTypeLocator());
        }

        ThymeleafEvaluationContextACLTypeLocator(final TypeLocator typeLocator) {
            super();
            // typeLocator CAN be null
            this.typeLocator = typeLocator;
            if (this.typeLocator instanceof StandardTypeLocator) {
                // A default prefix on "java.lang" is added by default, but we will remove it in order to avoid
                // the filter forbidding all "java.lang.*" classes to be bypassed.
                ((StandardTypeLocator)this.typeLocator).removeImport("java.lang");
            }
        }

        @Override
        public Class<?> findType(final String typeName) throws EvaluationException {
            if (this.typeLocator == null) {
                throw new EvaluationException("Type could not be located (no type locator configured): " + typeName);
            }
            if (!ExpressionUtils.isTypeAllowed(typeName)) {
                throw new EvaluationException(
                        String.format("Access is forbidden for type '%s' in this expression context.", typeName));
            }
            return this.typeLocator.findType(typeName);
        }

    }



    static final class ThymeleafEvaluationContextACLPropertyAccessor extends ReflectivePropertyAccessor {

        private final ReflectivePropertyAccessor propertyAccessor;

        ThymeleafEvaluationContextACLPropertyAccessor() {
            this(null);
        }

        ThymeleafEvaluationContextACLPropertyAccessor(final ReflectivePropertyAccessor propertyAccessor) {
            super(false); // allowWrite = false
            // propertyAccessor CAN be null
            this.propertyAccessor = propertyAccessor;
        }


        @Override
        public boolean canRead(final EvaluationContext context, final Object targetObject, final String name) throws AccessException {

            final boolean canRead;
            if (this.propertyAccessor != null) {
                canRead =  this.propertyAccessor.canRead(context, targetObject, name);
            } else {
                canRead = super.canRead(context, targetObject, name);
            }

            if (canRead) {
                // We need to perform the check on the getter equivalent to the member being called
                final String methodEquiv =
                        ("empty".equals(name) || "blank".equals(name)) ?
                            "is" + Character.toUpperCase(name.charAt(0)) + name.substring(1) :
                            "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1);

                if (!ExpressionUtils.isMemberAllowed(targetObject, methodEquiv)) {
                    throw new EvaluationException(
                            String.format(
                                    "Accessing member '%s' is forbidden for type '%s' in this expression context.",
                                    name, targetObject.getClass()));
                }
            }

            return canRead;

        }

    }



    static final class ThymeleafEvaluationContextACLMethodResolver extends ReflectiveMethodResolver {

        private final ReflectiveMethodResolver methodResolver;

        ThymeleafEvaluationContextACLMethodResolver() {
            this(null);
        }

        ThymeleafEvaluationContextACLMethodResolver(final ReflectiveMethodResolver methodResolver) {
            super();
            // methodResolver CAN be null
            this.methodResolver = methodResolver;
        }

        @Override
        public MethodExecutor resolve(
                final EvaluationContext context, final Object targetObject,
                final String name, final List<TypeDescriptor> argumentTypes) throws AccessException {

            final MethodExecutor methodExecutor;
            if (this.methodResolver != null) {
                methodExecutor = this.methodResolver.resolve(context, targetObject, name, argumentTypes);
            } else {
                methodExecutor = super.resolve(context, targetObject, name, argumentTypes);
            }

            if (methodExecutor != null) {
                if (!ExpressionUtils.isMemberAllowed(targetObject, name)) {
                    throw new EvaluationException(
                            String.format(
                                "Calling method '%s' is forbidden for type '%s' in this expression context.",
                                name, targetObject.getClass()));
                }
            }

            return methodExecutor;

        }

    }

}

        if (this.expressionObjects != null && this.expressionObjects.containsObject(name)) {
            final Object result = this.expressionObjects.getObject(name);
            if (result != null) {
                return result;
            }
        }
        // fall back to superclass
        return super.lookupVariable(name);
    
<methods>public void <init>() ,public void <init>(java.lang.Object) ,public void addConstructorResolver(org.springframework.expression.ConstructorResolver) ,public void addMethodResolver(org.springframework.expression.MethodResolver) ,public void addPropertyAccessor(org.springframework.expression.PropertyAccessor) ,public org.springframework.expression.BeanResolver getBeanResolver() ,public List<org.springframework.expression.ConstructorResolver> getConstructorResolvers() ,public List<org.springframework.expression.MethodResolver> getMethodResolvers() ,public org.springframework.expression.OperatorOverloader getOperatorOverloader() ,public List<org.springframework.expression.PropertyAccessor> getPropertyAccessors() ,public org.springframework.expression.TypedValue getRootObject() ,public org.springframework.expression.TypeComparator getTypeComparator() ,public org.springframework.expression.TypeConverter getTypeConverter() ,public org.springframework.expression.TypeLocator getTypeLocator() ,public java.lang.Object lookupVariable(java.lang.String) ,public void registerFunction(java.lang.String, java.lang.reflect.Method) ,public void registerMethodFilter(Class<?>, org.springframework.expression.MethodFilter) throws java.lang.IllegalStateException,public boolean removeConstructorResolver(org.springframework.expression.ConstructorResolver) ,public boolean removeMethodResolver(org.springframework.expression.MethodResolver) ,public boolean removePropertyAccessor(org.springframework.expression.PropertyAccessor) ,public void setBeanResolver(org.springframework.expression.BeanResolver) ,public void setConstructorResolvers(List<org.springframework.expression.ConstructorResolver>) ,public void setMethodResolvers(List<org.springframework.expression.MethodResolver>) ,public void setOperatorOverloader(org.springframework.expression.OperatorOverloader) ,public void setPropertyAccessors(List<org.springframework.expression.PropertyAccessor>) ,public void setRootObject(java.lang.Object) ,public void setRootObject(java.lang.Object, org.springframework.core.convert.TypeDescriptor) ,public void setTypeComparator(org.springframework.expression.TypeComparator) ,public void setTypeConverter(org.springframework.expression.TypeConverter) ,public void setTypeLocator(org.springframework.expression.TypeLocator) ,public void setVariable(java.lang.String, java.lang.Object) ,public void setVariables(Map<java.lang.String,java.lang.Object>) <variables>private org.springframework.expression.BeanResolver beanResolver,private volatile List<org.springframework.expression.ConstructorResolver> constructorResolvers,private volatile List<org.springframework.expression.MethodResolver> methodResolvers,private org.springframework.expression.OperatorOverloader operatorOverloader,private volatile List<org.springframework.expression.PropertyAccessor> propertyAccessors,private volatile org.springframework.expression.spel.support.ReflectiveMethodResolver reflectiveMethodResolver,private org.springframework.expression.TypedValue rootObject,private org.springframework.expression.TypeComparator typeComparator,private org.springframework.expression.TypeConverter typeConverter,private org.springframework.expression.TypeLocator typeLocator,private final Map<java.lang.String,java.lang.Object> variables