All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aspectran.thymeleaf.expression.ASELVariableExpressionEvaluator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2025 The Aspectran Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.aspectran.thymeleaf.expression;

import com.aspectran.core.activity.Activity;
import com.aspectran.core.context.asel.ExpressionParserException;
import com.aspectran.core.context.asel.TokenizedExpression;
import com.aspectran.core.context.asel.ognl.OgnlSupport;
import com.aspectran.thymeleaf.context.CurrentActivityHolder;
import com.aspectran.thymeleaf.context.UtilizedOgnlContext;
import com.aspectran.utils.annotation.jsr305.NonNull;
import com.aspectran.utils.logging.Logger;
import com.aspectran.utils.logging.LoggerFactory;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.IContext;
import org.thymeleaf.context.IExpressionContext;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.standard.expression.IStandardConversionService;
import org.thymeleaf.standard.expression.IStandardVariableExpression;
import org.thymeleaf.standard.expression.IStandardVariableExpressionEvaluator;
import org.thymeleaf.standard.expression.SelectionVariableExpression;
import org.thymeleaf.standard.expression.StandardExpressionExecutionContext;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.thymeleaf.standard.expression.VariableExpression;
import org.thymeleaf.standard.util.StandardExpressionUtils;

import static com.aspectran.thymeleaf.expression.OgnlContextPropertyAccessor.RESTRICT_REQUEST_PARAMETERS;

/**
 * Evaluator for variable expressions ({@code ${...}}) in Thymeleaf Standard Expressions, using the
 * Aspectran expression language.
 *
 * 

Created: 2024. 11. 23.

*/ public class ASELVariableExpressionEvaluator implements IStandardVariableExpressionEvaluator { private static final Logger logger = LoggerFactory.getLogger(ASELVariableExpressionEvaluator.class); private static final String EXPRESSION_CACHE_TYPE_OGNL = "asel"; private final boolean applyOgnlShortcuts; public ASELVariableExpressionEvaluator(boolean applyOgnlShortcuts) { this.applyOgnlShortcuts = applyOgnlShortcuts; /* * INITIALIZE AND REGISTER THE PROPERTY ACCESSOR */ OgnlContextPropertyAccessor accessor = new OgnlContextPropertyAccessor(); OgnlRuntime.setPropertyAccessor(IContext.class, accessor); } @Override public Object evaluate( IExpressionContext context, IStandardVariableExpression expression, StandardExpressionExecutionContext exeContext) { return evaluate(context, expression, exeContext, applyOgnlShortcuts); } private static Object evaluate( IExpressionContext context, IStandardVariableExpression expression, StandardExpressionExecutionContext exeContext, boolean applyOgnlShortcuts) { if (logger.isTraceEnabled()) { logger.trace("[THYMELEAF][" + TemplateEngine.threadIndex() + "] AspectranEL expression: evaluating expression \"" + expression.getExpression() + "\" on target"); } try { String expressionStr = expression.getExpression(); if (expressionStr == null) { throw new TemplateProcessingException("Expression content is null, which is not allowed"); } Activity activity = (context instanceof CurrentActivityHolder holder ? holder.getActivity() : null); IEngineConfiguration configuration = context.getConfiguration(); boolean useSelectionAsRoot = expression.getUseSelectionAsRoot(); ComputedOgnlExpression parsedExpression = obtainComputedOgnlExpression( activity, configuration, expression, expressionStr, exeContext, applyOgnlShortcuts); OgnlContext contextVariables = resolveContextVariables(context, exeContext, parsedExpression); // The root object on which we will evaluate expressions will depend on whether a selection target is // active or not... ITemplateContext templateContext = (context instanceof ITemplateContext tc ? tc : null); Object root = (useSelectionAsRoot && templateContext != null && templateContext.hasSelectionTarget() ? templateContext.getSelectionTarget() : templateContext); if (root != null) { contextVariables.setRoot(root); } // Execute the expression! Object result; try { result = executeExpression(activity, configuration, parsedExpression.expression, contextVariables); } catch (OgnlShortcutExpression.OGNLShortcutExpressionNotApplicableException notApplicable) { // We tried to apply shortcuts, but it is not possible for this expression even if it parsed OK, // so we need to empty the cache and try again disabling shortcuts. Once processed for the first time, // an OGNL (non-shortcut) parsed expression will already be cached and this exception will not be // thrown again invalidateComputedOgnlExpression(configuration, expression, expressionStr); return evaluate(context, expression, exeContext, false); } if (exeContext.getPerformTypeConversion()) { IStandardConversionService conversionService = StandardExpressions.getConversionService(configuration); return conversionService.convert(context, result, String.class); } else { return result; } } catch (Exception e) { throw new TemplateProcessingException( "Exception evaluating OGNL expression: \"" + expression.getExpression() + "\"", e); } } @NonNull private static OgnlContext resolveContextVariables( IExpressionContext context, StandardExpressionExecutionContext exeContext, @NonNull ComputedOgnlExpression parsedExpression) { OgnlContext contextVariables; if (parsedExpression.mightNeedExpressionObjects) { // The IExpressionObjects implementation returned by processing contexts that include the Standard // Dialects will be lazy in the creation of expression objects (i.e. they won't be created until really // needed). And in order for this behaviour to be accepted by OGNL, we will be wrapping this object // inside an implementation of Map, which will afterwards be fed to the constructor // of an OgnlContext object. // Note this will never happen with shortcut expressions, as the '#' character with which all // expression object names start is not allowed by the OgnlShortcutExpression parser. contextVariables = new UtilizedOgnlContext(context.getExpressionObjects()); } else { contextVariables = OgnlSupport.createDefaultContext(); } // We might need to apply restrictions on the request parameters. In the case of OGNL, the only way we // can actually communicate with the PropertyAccessor, (OGNLVariablesMapPropertyAccessor), which is the // agent in charge of applying such restrictions, is by adding a context variable that the property accessor // can later lookup during evaluation. if (exeContext.getRestrictVariableAccess()) { contextVariables.put(RESTRICT_REQUEST_PARAMETERS, RESTRICT_REQUEST_PARAMETERS); } return contextVariables; } private static ComputedOgnlExpression obtainComputedOgnlExpression( Activity activity, IEngineConfiguration configuration, IStandardVariableExpression expression, String expressionStr, @NonNull StandardExpressionExecutionContext exeContext, boolean applyOgnlShortcuts) throws ExpressionParserException { // If restrictions apply, we want to avoid applying shortcuts so that we delegate to OGNL validation // of method calls and references to allowed classes. boolean doApplyOgnlShortcuts = applyOgnlShortcuts && !exeContext.getRestrictVariableAccess() && !exeContext.getRestrictInstantiationAndStatic(); if (exeContext.getRestrictInstantiationAndStatic() && StandardExpressionUtils.containsOGNLInstantiationOrStaticOrParam(expressionStr)) { throw new TemplateProcessingException( "Instantiation of new objects and access to static classes or parameters is forbidden in this context"); } if (expression instanceof VariableExpression ve) { Object cachedExpression = ve.getCachedExpression(); if (cachedExpression instanceof ComputedOgnlExpression coe) { return coe; } cachedExpression = parseComputedOgnlExpression(activity, configuration, expressionStr, doApplyOgnlShortcuts); ve.setCachedExpression(cachedExpression); return (ComputedOgnlExpression)cachedExpression; } if (expression instanceof SelectionVariableExpression sve) { Object cachedExpression = sve.getCachedExpression(); if (cachedExpression instanceof ComputedOgnlExpression coe) { return coe; } cachedExpression = parseComputedOgnlExpression(activity, configuration, expressionStr, doApplyOgnlShortcuts); sve.setCachedExpression(cachedExpression); return (ComputedOgnlExpression)cachedExpression; } return parseComputedOgnlExpression(activity, configuration, expressionStr, doApplyOgnlShortcuts); } @NonNull private static ComputedOgnlExpression parseComputedOgnlExpression( Activity activity, IEngineConfiguration configuration, String expressionStr, boolean applyOGNLShortcuts) throws ExpressionParserException { ComputedOgnlExpression parsedExpression = ExpressionCache.getFromCache(configuration, expressionStr, EXPRESSION_CACHE_TYPE_OGNL); if (parsedExpression != null) { return parsedExpression; } // The result of parsing might be an OGNL expression AST or a ShortcutOGNLExpression (for simple cases) parsedExpression = parseExpression(activity, expressionStr, applyOGNLShortcuts); ExpressionCache.putIntoCache(configuration, expressionStr, parsedExpression, EXPRESSION_CACHE_TYPE_OGNL); return parsedExpression; } private static void invalidateComputedOgnlExpression( IEngineConfiguration configuration, IStandardVariableExpression expression, String expressionStr) { if (expression instanceof VariableExpression ve) { ve.setCachedExpression(null); } else if (expression instanceof SelectionVariableExpression ve) { ve.setCachedExpression(null); } ExpressionCache.removeFromCache(configuration, expressionStr, EXPRESSION_CACHE_TYPE_OGNL); } @NonNull private static ComputedOgnlExpression parseExpression( Activity activity, String expressionStr, boolean applyOgnlShortcuts) throws ExpressionParserException { if (applyOgnlShortcuts) { String[] expressions = OgnlShortcutExpression.parse(expressionStr); if (expressions != null) { OgnlShortcutExpression ose = new OgnlShortcutExpression(expressions); boolean mightNeedExpressionObjects = StandardExpressionUtils.mightNeedExpressionObjects(expressionStr); return new ComputedOgnlExpression(ose, mightNeedExpressionObjects); } } boolean mightNeedExpressionObjects = false; Object parsedExpression; if (activity != null) { TokenizedExpression tokenizedExpression = new TokenizedExpression(expressionStr); String substitutedExpression = tokenizedExpression.getSubstitutedExpression(); if (substitutedExpression != null) { mightNeedExpressionObjects = StandardExpressionUtils.mightNeedExpressionObjects(substitutedExpression); } parsedExpression = tokenizedExpression; } else { try { parsedExpression = Ognl.parseExpression(expressionStr); } catch (OgnlException e) { throw new ExpressionParserException(expressionStr, e); } mightNeedExpressionObjects = StandardExpressionUtils.mightNeedExpressionObjects(expressionStr); } return new ComputedOgnlExpression(parsedExpression, mightNeedExpressionObjects); } private static Object executeExpression( Activity activity, IEngineConfiguration configuration, Object parsedExpression, OgnlContext contextVariables) throws Exception { if (parsedExpression instanceof OgnlShortcutExpression ose) { return ose.evaluate(configuration, contextVariables, contextVariables.getRoot()); } if (activity != null && parsedExpression instanceof TokenizedExpression te) { return te.evaluate(activity, contextVariables, contextVariables.getRoot()); } else { return Ognl.getValue(parsedExpression, contextVariables, contextVariables.getRoot()); } } private static class ComputedOgnlExpression { Object expression; boolean mightNeedExpressionObjects; ComputedOgnlExpression(Object expression, boolean mightNeedExpressionObjects) { this.expression = expression; this.mightNeedExpressionObjects = mightNeedExpressionObjects; } } @Override public String toString() { return "AspectranEL"; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy