利用递归,加强spel表达式中的三元操作符

关于spel表达式参照我另一篇文章:SPEL 表达式在自定义注解中的应用

package com.haoyu.sip.utils.spring;

import com.haoyu.sip.utils.StringUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.*;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Pattern;

/**
 * SPEL表达式解析工具类
 *
 * @author trumans
 */
@Component
public class SpelPaserUtil {
    
    private static Logger logger = LoggerFactory.getLogger(SpelPaserUtil.class);
    
    /**
     * 确保#{}整个捕获
     */
    private static final Pattern ELVIS_PATTERN = Pattern.compile("(#\\{(.*?)\\?(.*?):(.*?[\\u4e00-\\u9fa5>\\w!@#$%^&*_+=])})");
    
    private static final Pattern IS_ELVIS_PATTERN = Pattern.compile(".*?\\?.*?:.*");
    
    private static final String PREFIX = "#{";
    
    private static final String SUFFIX = "}";
    
    private static ExpressionParser parser = new SpelExpressionParser();
    
    /**
     * 标准表达式环境上下文,字符串只能是纯spel表达式,不能是夹杂spel表达式的字符串
     */
    private static StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
    
    /**
     * 字符串模板的解析器上下文
     */
    private static TemplateParserContext templateParserContext = new TemplateParserContext();
    
    private static ApplicationContext applicationContext;
    
    /**
     * spel表达式获取spring容器中的bean的关键
     */
    private static BeanFactoryResolver beanFactoryResolver;
    
    @Autowired(required = false)
    public void setApplicationContext(ApplicationContext applicationContext) {
        if (SpelPaserUtil.applicationContext == null && null != applicationContext) {
            SpelPaserUtil.applicationContext = applicationContext;
        }
        if (null == SpelPaserUtil.beanFactoryResolver && SpelPaserUtil.applicationContext != null) {
            /*如果执行了,就可以使用@beanName方式获取容器中的bean。详见spel语法*/
            SpelPaserUtil.beanFactoryResolver = new BeanFactoryResolver(SpelPaserUtil.applicationContext);
            SpelPaserUtil.evaluationContext.setBeanResolver(SpelPaserUtil.beanFactoryResolver);
        }
    }
    
    /**
     * 基本方法
     *
     * @param expressionStr
     * @param root
     * @param desiredResultType
     * @param parserContext
     * @param 
     * @return
     */
    public static  T parseExpression(String expressionStr, Object root, Class desiredResultType, EvaluationContext evaluationContext, ParserContext parserContext) {
        T value = null;
        try {
            Expression expression = SpelPaserUtil.parser.parseExpression(expressionStr, parserContext);
            if (evaluationContext instanceof StandardEvaluationContext && null == evaluationContext.getBeanResolver()) {
//          保证非内置StandardEvaluationContext的evaluationContext可以使用spring容器中的bean
                ((StandardEvaluationContext) evaluationContext).setBeanResolver(SpelPaserUtil.beanFactoryResolver);
            }
            value = expression.getValue(evaluationContext, root, desiredResultType);
        }catch (Exception e){
//          logger.error("{} {} {}",e.getClass().getSimpleName(),e.getMessage(),e.getStackTrace());
        }
        return value;
    }
    
    public static  T parseExpression(String expressionStr, Map rootMap, Class valueType) {
        T parserResult = null;
        if (MapUtils.isNotEmpty(rootMap)) {
            StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariables(rootMap);
            parserResult = parseExpression(expressionStr, rootMap, valueType, evaluationContext, null);
        } else {
            parserResult = parseExpression(expressionStr, valueType);
        }
        return parserResult;
    }
    
    public static String parseExpression(String expressionStr, Map rootMap) {
        return parseExpression(expressionStr, rootMap, String.class);
    }
    
    public static  T parseExpression(String expressionStr, Object root, Class desiredResultType) {
        return parseExpression(expressionStr, root, desiredResultType, SpelPaserUtil.evaluationContext, null);
    }
    
    public static  T parseExpression(String expressionStr, Class desiredResultType) {
        return parseExpression(expressionStr, TypedValue.NULL.getValue(), desiredResultType);
    }
    
    public static String parseExpression(String expressionStr, Object root) {
        return parseExpression(expressionStr, root, String.class);
    }
    
    public static String parseExpression(String expressionStr) {
        return parseExpression(expressionStr, TypedValue.NULL.getValue());
    }
    
    //-------------------template parser context method--------------------
    
    public static String parseTemplateExpression(String expressionStr, Object root, EvaluationContext evaluationContext) {
        return parseExpression(expressionStr, root, String.class, evaluationContext, SpelPaserUtil.templateParserContext);
    }
    
    public static String parseTemplateExpression(String expressionStr, Object root) {
        return parseTemplateExpression(expressionStr, root, SpelPaserUtil.evaluationContext);
    }
    
    public static String parseTemplateExpression(String expressionStr) {
        return parseTemplateExpression(expressionStr, TypedValue.NULL.getValue());
    }
    
    /**
     * 处理spel模板字符串。例如: this is #{#name} 。#{和}定界符将spel表达式嵌套在了字符串中
     *
     * @param expressionStr
     * @param rootMap
     * @return
     * @throws Exception
     */
    public static String parseTemplateExpression(String expressionStr, Map rootMap) {
        String parserResult = expressionStr;
        if (MapUtils.isNotEmpty(rootMap)) {
            StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariables(rootMap);
            parserResult = parseTemplateExpression(expressionStr, rootMap, evaluationContext);
        } else {
            parserResult = parseTemplateExpression(expressionStr);
        }
        return parserResult;
    }
    
    /**
     * 用来解决spel表达式在三元操作符的局限性(即无法在三元操作符中的true or false 部分继续书写spel template模板表达式)
     * 三元操作的logic expression部分,使用非TemplateExpression,true or false 部分可以继续使用TemplateExpression一直递归。但必须保证,整个三元操作符是TemplateExpression。
     * 已无效:注意:#{ 与 } 前后千万别有空格 因为正则表达式没有进行配对匹配。后期可能修正,未知;
     * 可供参考,加以修改
     * @param expressionStr
     * @param rootMap
     * @return
     * @throws Exception
     */
    public static String elvisRecursive(String expressionStr, Map rootMap) {
        try {
            Stack logicSymbol = new Stack();
            List coordinate = new ArrayList<>();
            int ti = expressionStr.indexOf("?", 0);
            if (ti != -1) {
                logicSymbol.push(ti);
                coordinate.add(ti);
            }
            for (int i = ti + 1; i < expressionStr.length(); i++) {
                /*确定正确的?:位置*/
                if (!logicSymbol.empty()) {
                    if (StringUtils.equals("?", String.valueOf(expressionStr.charAt(i)))) {
                        /*如果logicSymbol栈中没有了元素,就表明当前coordinate栈中存放的就是?和:的位置*/
                        logicSymbol.push(i);
                        coordinate.add(i);
                    } else if (StringUtils.equals(":", String.valueOf(expressionStr.charAt(i)))) {
                        coordinate.add(i);
                        logicSymbol.pop();
                    }
                } else {
                    break;
                }
            }
            if (CollectionUtils.isNotEmpty(coordinate) && coordinate.size() > 1) {
                coordinate.set(1, coordinate.get(coordinate.size() - 1));
                /*保证表达式有递归Elvis表达式,因为从?号之前的字符串中最末尾的 #{ 标志就是整个模板开始*/
                int prefixIndex = expressionStr.lastIndexOf(PREFIX, coordinate.get(0));
                /*prefixStack 保存 #{ 或者 { 的位置 */
                Stack prefixStack = new Stack();
                /*suffixStack保存 } 的位置*/
                Stack suffixStack = new Stack();
                for (int j = coordinate.get(1); j < expressionStr.length(); ) {
                    /*整体算法是,每找到一个#{ 或者 { ,向prefixStack其中加入一个值;每找到一个suffixStack,则向suffixStack加入,同时将prefixStack弹出一个;如果prefixStack为空时,就是匹配的 } 位置*/
                    if (StringUtils.equals(String.valueOf(expressionStr.charAt(j)), SUFFIX)) {
                        if (prefixStack.empty()) {
                            suffixStack.push(j);
                            break;
                        } else {
                            prefixStack.pop();
                        }
                        j = j + SUFFIX.length();
                    } else if (j < expressionStr.length() - 1 && StringUtils.equals(String.valueOf(new char[]{expressionStr.charAt(j), expressionStr.charAt(j + 1)}), PREFIX)) {
                        /*确保可以在表达式中使用内建map表达式*/
                        prefixStack.push(j);
                        j = j + PREFIX.length();
                    } else if (StringUtils.equals(String.valueOf(expressionStr.charAt(j)), "{")) {
                        /*确保可以在表达式中使用内建map表达式*/
                        prefixStack.push(j);
                        j += 1;
                    } else {
                        j += 1;
                    }
                }
                int suffixIndex = suffixStack.pop();
                /*分离出 logic ? true : false 三元表达式中 logic,true,false 三部分*/
                String elvisExp = StringUtils.subNoException(expressionStr, prefixIndex, suffixIndex + 1);
                String logicExp = StringUtils.subNoException(expressionStr, prefixIndex + PREFIX.length(), coordinate.get(0));
                String trueExp = StringUtils.subNoException(expressionStr, coordinate.get(0) + 1, coordinate.get(1));
                String falseExp = StringUtils.subNoException(expressionStr, coordinate.get(1) + 1, suffixIndex);
                String subElvisResult = "";
                String logicRs = SpelPaserUtil.parseExpression(logicExp, rootMap);
                logicRs = logicRs == null ? "true" : logicRs;
                if (Boolean.valueOf(logicRs)) {
                    subElvisResult = elvisRecursive(trueExp, rootMap);
                } else {
                    subElvisResult = elvisRecursive(falseExp, rootMap);
                }
                int elvisIndex = expressionStr.indexOf(elvisExp, 0);
                expressionStr = expressionStr.replace(elvisExp, subElvisResult);
                if (IS_ELVIS_PATTERN.matcher(expressionStr).find(elvisIndex)) {
                    /*如果还有三元表达式,继续解析*/
                    subElvisResult = elvisRecursive(expressionStr, rootMap);
                    expressionStr = subElvisResult;/*如果已经到了这条代码,就证明在整个表达式中Elvis已经没有了,并且全部的SPEL解析完成了*/
                }
            }
            expressionStr = SpelPaserUtil.parseTemplateExpression(expressionStr, rootMap);
            return expressionStr;
        } catch (Throwable e) {
//          logger.info("elvis happend error . current expression is {}", expressionStr);
//          e.printStackTrace();
        }
        return "";
    }
    
}

你可能感兴趣的:(利用递归,加强spel表达式中的三元操作符)